From 89e60e7275a33c38847cd19d01c0ee48727bc693 Mon Sep 17 00:00:00 2001 From: Demian Sempel Date: Tue, 9 Jul 2024 16:17:27 +0200 Subject: [PATCH 01/23] Replace axios with node-fetch --- package.json | 2 + src/Options.ts | 6 +- .../ProfileGiftcardIssuersBinder.ts | 2 +- src/communication/NetworkClient.ts | 121 +++++++++--------- src/data/methods/data.ts | 1 - src/data/payments/data.ts | 18 ++- src/errors/ApiError.ts | 9 +- tests/__nock-fixtures__/paymentLinks.json | 39 ++++++ tests/paymentLinks/paymentLinks.test.ts | 4 + tests/unit/resources/methods.test.ts | 4 +- yarn.lock | 33 +++++ 11 files changed, 165 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index 1cf43859..23a9e466 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "axios": "^1.6.2", + "node-fetch": "^2.7.0", "ruply": "^1.0.1" }, "devDependencies": { @@ -51,6 +52,7 @@ "@mollie/eslint-config-typescript": "^1.6.5", "@types/jest": "^29.5.11", "@types/node": "^18.14.6", + "@types/node-fetch": "^2.6.11", "axios-mock-adapter": "1.19.0", "babel-jest": "^29.5.0", "commitizen": "^4.3.0", diff --git a/src/Options.ts b/src/Options.ts index 42156409..5db0f951 100644 --- a/src/Options.ts +++ b/src/Options.ts @@ -1,5 +1,3 @@ -import { type AxiosRequestConfig } from 'axios'; - import type Xor from './types/Xor'; type Options = Xor< @@ -29,7 +27,7 @@ type Options = Xor< * The URL of the root of the Mollie API. Default: `'https://api.mollie.com:443/v2/'`. */ apiEndpoint?: string; -} & Pick; +}; const falsyDescriptions = new Map([ [undefined, 'undefined'], @@ -46,7 +44,7 @@ function describeFalsyOption(options: Options, key: keyof Options) { return null; } return `Parameter "${key}" is ${falsyDescriptions.get(options[key]) ?? options[key]}.`; -}; +} /** * Throws a `TypeError` if the passed options object does not contain an `apiKey` or an `accessToken`. diff --git a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts index cd61e594..6311ebcb 100644 --- a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts +++ b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts @@ -1,5 +1,5 @@ import type TransformingNetworkClient from '../../../communication/TransformingNetworkClient'; -import {type IssuerData} from '../../../data/issuer/IssuerModel'; +import { type IssuerData } from '../../../data/issuer/IssuerModel'; import type IssuerModel from '../../../data/issuer/IssuerModel'; import ApiError from '../../../errors/ApiError'; import renege from '../../../plumbing/renege'; diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 9f248820..4a6af926 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -1,7 +1,6 @@ import https from 'https'; import { type SecureContextOptions } from 'tls'; - -import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, type RawAxiosRequestHeaders } from 'axios'; +import fetch, { type RequestInit, type Response } from 'node-fetch'; import { run } from 'ruply'; import type Page from '../data/page/Page'; @@ -15,7 +14,7 @@ import { type IdempotencyParameter } from '../types/parameters'; import breakUrl from './breakUrl'; import buildUrl, { type SearchParameters } from './buildUrl'; import dromedaryCase from './dromedaryCase'; -import makeRetrying, { idempotencyHeaderName } from './makeRetrying'; +import { idempotencyHeaderName } from './makeRetrying'; import fling from '../plumbing/fling'; /** @@ -69,13 +68,25 @@ const throwApiError = run( }, findProperty => function throwApiError(cause: unknown) { - if (findProperty(cause, 'response') && cause.response != undefined) { - throw ApiError.createFromResponse(cause.response as AxiosResponse); - } throw new ApiError(findProperty(cause, 'message') ? String(cause.message) : 'An unknown error has occurred'); }, ); +/** + * Checks if an API error needs to be thrown based on the passed result. + */ +async function processFetchResponse(res: Response) { + // Request was successful, but no content was returned. + if (res.status == 204) return true; + // Request was successful and content was returned. + const body = await res.json(); + if (res.status >= 200 && res.status < 300) return body; + // Request was not successful, but the response body contains an error message. + if (body) throw ApiError.createFromResponse(body, res.headers); + // Request was not successful. + throw new ApiError('An unknown error has occurred'); +} + interface Data {} interface Context {} @@ -83,7 +94,7 @@ interface Context {} * This class is essentially a wrapper around axios. It simplifies communication with the Mollie API over the network. */ export default class NetworkClient { - protected readonly axiosInstance: AxiosInstance; + protected readonly request: (pathname: string, options?: RequestInit) => Promise; constructor({ apiKey, accessToken, @@ -92,30 +103,28 @@ export default class NetworkClient { caCertificates, libraryVersion, nodeVersion, - ...axiosOptions }: Options & { caCertificates?: SecureContextOptions['ca']; libraryVersion: string; nodeVersion: string }) { - axiosOptions.headers = { ...axiosOptions.headers }; // Compose the headers set in the sent requests. - axiosOptions.headers['User-Agent'] = composeUserAgent(nodeVersion, libraryVersion, versionStrings); + const headers: Record = {}; + headers['User-Agent'] = composeUserAgent(nodeVersion, libraryVersion, versionStrings); if (apiKey != undefined) { - axiosOptions.headers['Authorization'] = `Bearer ${apiKey}`; + headers['Authorization'] = `Bearer ${apiKey}`; } /* if (accessToken != undefined) */ else { - axiosOptions.headers['Authorization'] = `Bearer ${accessToken}`; - axiosOptions.headers['User-Agent'] += ' OAuth/2.0'; + headers['Authorization'] = `Bearer ${accessToken}`; + headers['User-Agent'] += ' OAuth/2.0'; } - axiosOptions.headers['Accept'] = 'application/hal+json'; - axiosOptions.headers['Accept-Encoding'] = 'gzip'; - axiosOptions.headers['Content-Type'] = 'application/json'; - // Create the Axios instance. - this.axiosInstance = axios.create({ - ...axiosOptions, - baseURL: apiEndpoint, - httpsAgent: new https.Agent({ - ca: caCertificates, - }), - }); - // Make the Axios instance request multiple times is some scenarios. - makeRetrying(this.axiosInstance); + headers['Accept'] = 'application/hal+json'; + headers['Accept-Encoding'] = 'gzip'; + headers['Content-Type'] = 'application/json'; + + // Create the https agent. + const agent = new https.Agent({ ca: caCertificates }); + + // Create the request function. + this.request = (pathname, options) => fetch(`${apiEndpoint}${pathname}`, { agent, ...options, headers: { ...headers, ...options?.headers } }); + + // Make the Axios instance request multiple times in some scenarios. + // makeRetrying(this.axiosInstance); } async post(pathname: string, data: Data & IdempotencyParameter, query?: SearchParameters): Promise { @@ -123,29 +132,24 @@ export default class NetworkClient { // idempotency key in a separate argument instead of cramming it into the data like this. However, having a // separate argument would require every endpoint to split the input into those two arguments and thus cause a lot // of boiler-plate code. - let config: AxiosRequestConfig | undefined = undefined; - if (data.idempotencyKey != undefined) { - const { idempotencyKey, ...rest } = data; - config = { headers: { [idempotencyHeaderName]: idempotencyKey } }; - data = rest; - } - const response = await this.axiosInstance.post(buildUrl(pathname, query), data, config).catch(throwApiError); - if (response.status == 204) { - return true; - } - return response.data; + const { idempotencyKey, ...body } = data; + const config: RequestInit = { + method: 'POST', + headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, + body: JSON.stringify(body), + }; + return this.request(buildUrl(pathname, query), config).then(processFetchResponse).catch(throwApiError); } async get(pathname: string, query?: SearchParameters): Promise { - const response = await this.axiosInstance.get(buildUrl(pathname, query)).catch(throwApiError); - return response.data; + return this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); } async list(pathname: string, binderName: string, query?: SearchParameters): Promise { - const response = await this.axiosInstance.get(buildUrl(pathname, query)).catch(throwApiError); + const data = await this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); try { /* eslint-disable-next-line no-var */ - var { _embedded: embedded } = response.data; + var { _embedded: embedded } = data; } catch (error) { throw new ApiError('Received unexpected response from the server'); } @@ -153,10 +157,10 @@ export default class NetworkClient { } async page(pathname: string, binderName: string, query?: SearchParameters): Promise, 'links' | 'count'>> { - const response = await this.axiosInstance.get(buildUrl(pathname, query)).catch(throwApiError); + const data = await this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); try { /* eslint-disable-next-line no-var */ - var { _embedded: embedded, _links: links, count } = response.data; + var { _embedded: embedded, _links: links, count } = data; } catch (error) { throw new ApiError('Received unexpected response from the server'); } @@ -196,16 +200,16 @@ export default class NetworkClient { // and valuesPerMinute is set to 100, all 250 values received values will be yielded before the (two-minute) // break. const throttler = new Throttler(valuesPerMinute); - const { axiosInstance } = this; + const { request } = this; return new HelpfulIterator( (async function* iterate() { let url = buildUrl(pathname, { ...query, limit: popLimit() }); while (true) { // Request and parse the page from the Mollie API. - const response = await axiosInstance.get(url).catch(throwApiError); + const data = await request(url).then(processFetchResponse).catch(throwApiError); try { /* eslint-disable-next-line no-var */ - var { _embedded: embedded, _links: links } = response.data; + var { _embedded: embedded, _links: links } = data; } catch (error) { throw new ApiError('Received unexpected response from the server'); } @@ -230,22 +234,21 @@ export default class NetworkClient { } async patch(pathname: string, data: Data): Promise { - const response = await this.axiosInstance.patch(pathname, data).catch(throwApiError); - return response.data; + const config: RequestInit = { + method: 'PATCH', + body: JSON.stringify(data), + }; + return this.request(buildUrl(pathname), config).then(processFetchResponse).catch(throwApiError); } async delete(pathname: string, context?: Context & IdempotencyParameter): Promise { // Take the idempotency key from the context, if any. - let headers: RawAxiosRequestHeaders | undefined = undefined; - if (context?.idempotencyKey != undefined) { - const { idempotencyKey, ...rest } = context; - headers = { [idempotencyHeaderName]: idempotencyKey }; - context = rest; - } - const response = await this.axiosInstance.delete(pathname, { data: context, headers }).catch(throwApiError); - if (response.status == 204) { - return true; - } - return response.data as R; + const { idempotencyKey, ...body } = context ?? {}; + const config: RequestInit = { + method: 'DELETE', + headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, + body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined, + }; + return this.request(buildUrl(pathname), config).then(processFetchResponse).catch(throwApiError); } } diff --git a/src/data/methods/data.ts b/src/data/methods/data.ts index d9d6b701..eba4df75 100644 --- a/src/data/methods/data.ts +++ b/src/data/methods/data.ts @@ -76,7 +76,6 @@ export enum MethodStatus { rejected = 'rejected', } - export enum MethodImageSize { size1x = 'size1x', size2x = 'size2x', diff --git a/src/data/payments/data.ts b/src/data/payments/data.ts index 99511b9d..31c61c37 100644 --- a/src/data/payments/data.ts +++ b/src/data/payments/data.ts @@ -1,9 +1,23 @@ import type Nullable from '../../types/Nullable'; import { type ChargebackData } from '../chargebacks/Chargeback'; -import { type Address, type Amount, type ApiMode, type CardAudience, type CardFailureReason, type CardLabel, type FeeRegion, type HistoricPaymentMethod, type Links, type Locale, type PaymentMethod, type SequenceType, type Url } from '../global'; +import { + type Address, + type Amount, + type ApiMode, + type CardAudience, + type CardFailureReason, + type CardLabel, + type FeeRegion, + type HistoricPaymentMethod, + type Links, + type Locale, + type PaymentMethod, + type SequenceType, + type Url, +} from '../global'; import type Model from '../Model'; import { type RefundData } from '../refunds/data'; -import { type CaptureData } from './captures/data' +import { type CaptureData } from './captures/data'; export interface PaymentData extends Model<'payment'> { /** diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts index 61ded5fd..6a16a36c 100644 --- a/src/errors/ApiError.ts +++ b/src/errors/ApiError.ts @@ -1,4 +1,4 @@ -import { type AxiosResponse } from 'axios'; +import { type Headers } from 'node-fetch'; import { idempotencyHeaderName } from '../communication/makeRetrying'; import { type Links, type Url } from '../data/global'; @@ -112,9 +112,8 @@ export default class ApiError extends Error { * * @since 3.0.0 */ - public static createFromResponse(response: AxiosResponse): ApiError { - const { detail, title, status: statusCode, field, _links: links } = response.data; - const { headers } = response.config; - return new ApiError(detail ?? 'Received an error without a message', { title, statusCode, field, links, idempotencyKey: headers?.[idempotencyHeaderName] as string | undefined }); + public static createFromResponse(body: any, headers: Headers): ApiError { + const { detail, title, status: statusCode, field, _links: links } = body; + return new ApiError(detail ?? 'Received an error without a message', { title, statusCode, field, links, idempotencyKey: headers.get(idempotencyHeaderName) ?? undefined }); } } diff --git a/tests/__nock-fixtures__/paymentLinks.json b/tests/__nock-fixtures__/paymentLinks.json index 1aa40e3e..752d02b1 100644 --- a/tests/__nock-fixtures__/paymentLinks.json +++ b/tests/__nock-fixtures__/paymentLinks.json @@ -114,5 +114,44 @@ "max-age=31536000; includeSubDomains; preload" ], "responseIsBinary": false + }, + { + "scope": "https://api.mollie.com:443", + "method": "GET", + "path": "/v2/payment-links/pl_fake_id", + "body": "", + "status": 404, + "response": { + "status": 404, + "title": "Not Found", + "detail": "The resource with the token \"pl_fake_id\" could not be found.", + "_links": { + "documentation": { + "href": "https://docs.mollie.com/overview/handling-errors", + "type": "text/html" + } + } + }, + "rawHeaders": [ + "date", + "Tue, 09 Jul 2024 12:54:46 GMT", + "Server", + "Mollie", + "Content-Length", + "214", + "content-type", + "application/hal+json", + "Via", + "1.1 google, 1.1 google", + "X-XSS-Protection", + "1; mode=block", + "Strict-Transport-Security", + "max-age=31536000; includeSubDomains; preload", + "Alt-Svc", + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000", + "Connection", + "close" + ], + "responseIsBinary": false } ] \ No newline at end of file diff --git a/tests/paymentLinks/paymentLinks.test.ts b/tests/paymentLinks/paymentLinks.test.ts index a814832d..529b0b8e 100644 --- a/tests/paymentLinks/paymentLinks.test.ts +++ b/tests/paymentLinks/paymentLinks.test.ts @@ -26,5 +26,9 @@ describe('paymentLinks', () => { expect(paymentLink.amount.value).toBe('6.99'); }); + test('paymentLinks error response', async () => { + await expect(mollieClient.paymentLinks.get('pl_fake_id')).rejects.toThrow('The resource with the token "pl_fake_id" could not be found.'); + }); + afterAll(() => networkMocker.cleanup()); }); diff --git a/tests/unit/resources/methods.test.ts b/tests/unit/resources/methods.test.ts index 69940336..1d2c1502 100644 --- a/tests/unit/resources/methods.test.ts +++ b/tests/unit/resources/methods.test.ts @@ -22,7 +22,7 @@ describe('methods', () => { mock.onGet(`/methods/${methodId}`).reply(200, response._embedded.methods[0], {}); mock.onGet('/methods/foo').reply(500, error, {}); - it('should return a method instance', () => + it('should return a method instance', () => methods.get(methodId).then(result => { expect(result).toMatchSnapshot(); })); @@ -35,7 +35,7 @@ describe('methods', () => { }); }); - it('should throw an error for non-existent IDs', () => + it('should throw an error for non-existent IDs', () => methods .get('foo') .then(result => expect(result).toBeUndefined()) diff --git a/yarn.lock b/yarn.lock index f6523203..fa88743c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1595,6 +1595,14 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/node-fetch@^2.6.11": + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node@*", "@types/node@^18.14.6": version "18.14.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.6.tgz#ae1973dd2b1eeb1825695bb11ebfb746d27e3e93" @@ -4150,6 +4158,13 @@ nock@^13.3.0: lodash "^4.17.21" propagate "^2.0.0" +node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4863,6 +4878,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-node@^10.8.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -5031,6 +5051,19 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" From d77565be0d8a460bc803b96f99eca280c24fc6aa Mon Sep 17 00:00:00 2001 From: Demian Sempel Date: Tue, 9 Jul 2024 17:16:32 +0200 Subject: [PATCH 02/23] Fix some tests --- src/binders/applePay/ApplePayBinder.ts | 2 +- src/binders/chargebacks/ChargebacksBinder.ts | 2 +- src/binders/customers/CustomersBinder.ts | 2 +- src/binders/customers/mandates/CustomerMandatesBinder.ts | 2 +- src/binders/customers/payments/CustomerPaymentsBinder.ts | 2 +- .../customers/subscriptions/CustomerSubscriptionsBinder.ts | 2 +- src/binders/methods/MethodsBinder.ts | 2 +- src/binders/onboarding/OnboardingBinder.ts | 2 +- src/binders/orders/OrdersBinder.ts | 2 +- src/binders/orders/orderlines/OrderLinesBinder.ts | 2 +- src/binders/orders/shipments/OrderShipmentsBinder.ts | 2 +- src/binders/organizations/OrganizationsBinder.ts | 2 +- src/binders/paymentLinks/PaymentLinksBinder.ts | 2 +- src/binders/payments/PaymentsBinder.ts | 2 +- src/binders/payments/captures/PaymentCapturesBinder.ts | 2 +- .../payments/chargebacks/PaymentChargebacksBinder.ts | 2 +- src/binders/payments/orders/OrderPaymentsBinder.ts | 2 +- src/binders/payments/refunds/PaymentRefundsBinder.ts | 2 +- src/binders/permissions/PermissionsBinder.ts | 2 +- src/binders/profiles/ProfilesBinder.ts | 2 +- .../giftcardIssuers/ProfileGiftcardIssuersBinder.ts | 2 +- src/binders/profiles/methods/ProfileMethodsBinder.ts | 2 +- .../profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts | 2 +- src/binders/refunds/RefundsBinder.ts | 2 +- src/binders/refunds/orders/OrderRefundsBinder.ts | 2 +- src/binders/settlements/SettlementsBinder.ts | 2 +- .../settlements/captures/SettlementCapturesBinder.ts | 2 +- .../settlements/chargebacks/SettlementChargebacksBinder.ts | 2 +- .../settlements/payments/SettlementPaymentsBinder.ts | 2 +- src/binders/settlements/refunds/SettlementRefundsBinder.ts | 2 +- src/binders/subscriptions/SubscriptionsBinder.ts | 2 +- .../subscriptions/payments/SubscriptionPaymentsBinder.ts | 2 +- src/communication/NetworkClient.ts | 6 +++++- 33 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/binders/applePay/ApplePayBinder.ts b/src/binders/applePay/ApplePayBinder.ts index 11775b91..17117cba 100644 --- a/src/binders/applePay/ApplePayBinder.ts +++ b/src/binders/applePay/ApplePayBinder.ts @@ -4,7 +4,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import { type RequestPaymentSessionParameters } from './parameters'; -const pathSegments = 'wallets/applepay/sessions'; +const pathSegments = '/wallets/applepay/sessions'; export default class ApplePayBinder { constructor(protected readonly networkClient: NetworkClient) {} diff --git a/src/binders/chargebacks/ChargebacksBinder.ts b/src/binders/chargebacks/ChargebacksBinder.ts index d71569e5..a58fab60 100644 --- a/src/binders/chargebacks/ChargebacksBinder.ts +++ b/src/binders/chargebacks/ChargebacksBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = 'chargebacks'; +const pathSegment = '/chargebacks'; export default class ChargebacksBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/customers/CustomersBinder.ts b/src/binders/customers/CustomersBinder.ts index a26f949a..d8d99672 100644 --- a/src/binders/customers/CustomersBinder.ts +++ b/src/binders/customers/CustomersBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type DeleteParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = 'customers'; +const pathSegment = '/customers'; export default class CustomersBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/customers/mandates/CustomerMandatesBinder.ts b/src/binders/customers/mandates/CustomerMandatesBinder.ts index 95633ad7..41d64c98 100644 --- a/src/binders/customers/mandates/CustomerMandatesBinder.ts +++ b/src/binders/customers/mandates/CustomerMandatesBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type RevokeParameters } from './parameters'; function getPathSegments(customerId: string) { - return `customers/${customerId}/mandates`; + return `/customers/${customerId}/mandates`; } export default class CustomerMandatesBinder extends Binder { diff --git a/src/binders/customers/payments/CustomerPaymentsBinder.ts b/src/binders/customers/payments/CustomerPaymentsBinder.ts index 420da5ff..e00187f0 100644 --- a/src/binders/customers/payments/CustomerPaymentsBinder.ts +++ b/src/binders/customers/payments/CustomerPaymentsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(customerId: string) { - return `customers/${customerId}/payments`; + return `/customers/${customerId}/payments`; } export default class CustomerPaymentsBinder extends Binder { diff --git a/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts b/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts index e1816103..3c79538c 100644 --- a/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts +++ b/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; function getPathSegments(customerId: string) { - return `customers/${customerId}/subscriptions`; + return `/customers/${customerId}/subscriptions`; } export default class CustomerSubscriptionsBinder extends Binder { diff --git a/src/binders/methods/MethodsBinder.ts b/src/binders/methods/MethodsBinder.ts index e178a9da..881b93ae 100644 --- a/src/binders/methods/MethodsBinder.ts +++ b/src/binders/methods/MethodsBinder.ts @@ -7,7 +7,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type GetParameters, type ListParameters } from './parameters'; -const pathSegment = 'methods'; +const pathSegment = '/methods'; export default class MethodsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/onboarding/OnboardingBinder.ts b/src/binders/onboarding/OnboardingBinder.ts index d70337fd..df3a61db 100644 --- a/src/binders/onboarding/OnboardingBinder.ts +++ b/src/binders/onboarding/OnboardingBinder.ts @@ -6,7 +6,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type SubmitParameters } from './parameters'; -const pathSegments = 'onboarding/me'; +const pathSegments = '/onboarding/me'; export default class OnboardingBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/orders/OrdersBinder.ts b/src/binders/orders/OrdersBinder.ts index c5101f6b..bd211fc9 100644 --- a/src/binders/orders/OrdersBinder.ts +++ b/src/binders/orders/OrdersBinder.ts @@ -10,7 +10,7 @@ import Binder from '../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; import alias from '../../plumbing/alias'; -export const pathSegment = 'orders'; +export const pathSegment = '/orders'; /** * The **Orders API** allows you to use Mollie for your order management. diff --git a/src/binders/orders/orderlines/OrderLinesBinder.ts b/src/binders/orders/orderlines/OrderLinesBinder.ts index f97cadcd..c88e2256 100644 --- a/src/binders/orders/orderlines/OrderLinesBinder.ts +++ b/src/binders/orders/orderlines/OrderLinesBinder.ts @@ -10,7 +10,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type UpdateParameters } from './parameters'; function getPathSegments(orderId: string) { - return `orders/${orderId}/lines`; + return `/orders/${orderId}/lines`; } export default class OrderLinesBinder extends Binder { diff --git a/src/binders/orders/shipments/OrderShipmentsBinder.ts b/src/binders/orders/shipments/OrderShipmentsBinder.ts index 5ac01fee..47ccdc23 100644 --- a/src/binders/orders/shipments/OrderShipmentsBinder.ts +++ b/src/binders/orders/shipments/OrderShipmentsBinder.ts @@ -10,7 +10,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type GetParameters, type ListParameters, type UpdateParameters } from './parameters'; export function getPathSegments(orderId: string) { - return `orders/${orderId}/shipments`; + return `/orders/${orderId}/shipments`; } export default class OrderShipmentsBinder extends Binder { diff --git a/src/binders/organizations/OrganizationsBinder.ts b/src/binders/organizations/OrganizationsBinder.ts index 79ff7541..fda0158e 100644 --- a/src/binders/organizations/OrganizationsBinder.ts +++ b/src/binders/organizations/OrganizationsBinder.ts @@ -7,7 +7,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import Binder from '../Binder'; -const pathSegment = 'organizations'; +const pathSegment = '/organizations'; export default class OrganizationsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/paymentLinks/PaymentLinksBinder.ts b/src/binders/paymentLinks/PaymentLinksBinder.ts index 3f155fa3..68c1ba48 100644 --- a/src/binders/paymentLinks/PaymentLinksBinder.ts +++ b/src/binders/paymentLinks/PaymentLinksBinder.ts @@ -9,7 +9,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type GetParameters, type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = 'payment-links'; +const pathSegment = '/payment-links'; export default class PaymentsLinksBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/payments/PaymentsBinder.ts b/src/binders/payments/PaymentsBinder.ts index 17f9feac..d9509e0b 100644 --- a/src/binders/payments/PaymentsBinder.ts +++ b/src/binders/payments/PaymentsBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = 'payments'; +const pathSegment = '/payments'; export default class PaymentsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/payments/captures/PaymentCapturesBinder.ts b/src/binders/payments/captures/PaymentCapturesBinder.ts index 2bec810e..50d30a33 100644 --- a/src/binders/payments/captures/PaymentCapturesBinder.ts +++ b/src/binders/payments/captures/PaymentCapturesBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `payments/${paymentId}/captures`; + return `/payments/${paymentId}/captures`; } export default class PaymentCapturesBinder extends Binder { diff --git a/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts b/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts index 521a27ac..9d215484 100644 --- a/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts +++ b/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `payments/${paymentId}/chargebacks`; + return `/payments/${paymentId}/chargebacks`; } export default class PaymentChargebacksBinder extends Binder { diff --git a/src/binders/payments/orders/OrderPaymentsBinder.ts b/src/binders/payments/orders/OrderPaymentsBinder.ts index 2ed1e7ef..a79cd4ad 100644 --- a/src/binders/payments/orders/OrderPaymentsBinder.ts +++ b/src/binders/payments/orders/OrderPaymentsBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type CreateParameters } from './parameters'; function getPathSegments(orderId: string) { - return `orders/${orderId}/payments`; + return `/orders/${orderId}/payments`; } export default class OrderPaymentsBinder extends Binder { diff --git a/src/binders/payments/refunds/PaymentRefundsBinder.ts b/src/binders/payments/refunds/PaymentRefundsBinder.ts index c223436b..4a393f00 100644 --- a/src/binders/payments/refunds/PaymentRefundsBinder.ts +++ b/src/binders/payments/refunds/PaymentRefundsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `payments/${paymentId}/refunds`; + return `/payments/${paymentId}/refunds`; } export default class PaymentRefundsBinder extends Binder { diff --git a/src/binders/permissions/PermissionsBinder.ts b/src/binders/permissions/PermissionsBinder.ts index 3e3478da..93312ee9 100644 --- a/src/binders/permissions/PermissionsBinder.ts +++ b/src/binders/permissions/PermissionsBinder.ts @@ -6,7 +6,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import Binder from '../Binder'; -const pathSegment = 'permissions'; +const pathSegment = '/permissions'; export default class PermissionsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/profiles/ProfilesBinder.ts b/src/binders/profiles/ProfilesBinder.ts index 24763900..924ebec0 100644 --- a/src/binders/profiles/ProfilesBinder.ts +++ b/src/binders/profiles/ProfilesBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type DeleteParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = 'profiles'; +const pathSegment = '/profiles'; export default class ProfilesBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts index 6311ebcb..298b5d10 100644 --- a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts +++ b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `profiles/${profileId}/methods/giftcard/issuers`; + return `/profiles/${profileId}/methods/giftcard/issuers`; } export default class ProfileGiftcardIssuersBinder extends Binder { diff --git a/src/binders/profiles/methods/ProfileMethodsBinder.ts b/src/binders/profiles/methods/ProfileMethodsBinder.ts index 026705cc..887fc6ec 100644 --- a/src/binders/profiles/methods/ProfileMethodsBinder.ts +++ b/src/binders/profiles/methods/ProfileMethodsBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `profiles/${profileId}/methods`; + return `/profiles/${profileId}/methods`; } export default class ProfileMethodsBinder extends Binder { diff --git a/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts b/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts index 3a1b924d..c89a5fd2 100644 --- a/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts +++ b/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `profiles/${profileId}/methods/voucher/issuers`; + return `/profiles/${profileId}/methods/voucher/issuers`; } export default class ProfileVoucherIssuersBinder extends Binder { diff --git a/src/binders/refunds/RefundsBinder.ts b/src/binders/refunds/RefundsBinder.ts index 4611f441..30760412 100644 --- a/src/binders/refunds/RefundsBinder.ts +++ b/src/binders/refunds/RefundsBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = 'refunds'; +const pathSegment = '/refunds'; export default class RefundsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/refunds/orders/OrderRefundsBinder.ts b/src/binders/refunds/orders/OrderRefundsBinder.ts index 5d44159b..0095de80 100644 --- a/src/binders/refunds/orders/OrderRefundsBinder.ts +++ b/src/binders/refunds/orders/OrderRefundsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(orderId: string) { - return `orders/${orderId}/refunds`; + return `/orders/${orderId}/refunds`; } export default class OrderRefundsBinder extends Binder { diff --git a/src/binders/settlements/SettlementsBinder.ts b/src/binders/settlements/SettlementsBinder.ts index 4d94fbe1..d8736250 100644 --- a/src/binders/settlements/SettlementsBinder.ts +++ b/src/binders/settlements/SettlementsBinder.ts @@ -7,7 +7,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = 'settlements'; +const pathSegment = '/settlements'; export default class SettlementsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/settlements/captures/SettlementCapturesBinder.ts b/src/binders/settlements/captures/SettlementCapturesBinder.ts index 61f66357..b9420c30 100644 --- a/src/binders/settlements/captures/SettlementCapturesBinder.ts +++ b/src/binders/settlements/captures/SettlementCapturesBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `settlements/${settlementId}/captures`; + return `/settlements/${settlementId}/captures`; } export default class SettlementCapturesBinder extends Binder { diff --git a/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts b/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts index 60e14bc2..4a1ec5fb 100644 --- a/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts +++ b/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `settlements/${settlementId}/chargebacks`; + return `/settlements/${settlementId}/chargebacks`; } export default class SettlementChargebacksBinder extends Binder { diff --git a/src/binders/settlements/payments/SettlementPaymentsBinder.ts b/src/binders/settlements/payments/SettlementPaymentsBinder.ts index 9783eb11..bd9bdd07 100644 --- a/src/binders/settlements/payments/SettlementPaymentsBinder.ts +++ b/src/binders/settlements/payments/SettlementPaymentsBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `settlements/${settlementId}/payments`; + return `/settlements/${settlementId}/payments`; } export default class SettlementPaymentsBinder extends Binder { diff --git a/src/binders/settlements/refunds/SettlementRefundsBinder.ts b/src/binders/settlements/refunds/SettlementRefundsBinder.ts index 68b71d00..4b53c92c 100644 --- a/src/binders/settlements/refunds/SettlementRefundsBinder.ts +++ b/src/binders/settlements/refunds/SettlementRefundsBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `settlements/${settlementId}/refunds`; + return `/settlements/${settlementId}/refunds`; } export default class SettlementRefundsBinder extends Binder { diff --git a/src/binders/subscriptions/SubscriptionsBinder.ts b/src/binders/subscriptions/SubscriptionsBinder.ts index a7a1a810..7b40edcd 100644 --- a/src/binders/subscriptions/SubscriptionsBinder.ts +++ b/src/binders/subscriptions/SubscriptionsBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = 'subscriptions'; +const pathSegment = '/subscriptions'; export default class SubscriptionsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts b/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts index 1c3643e8..a3fabd29 100644 --- a/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts +++ b/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(customerId: string, subscriptionId: string): string { - return `customers/${customerId}/subscriptions/${subscriptionId}/payments`; + return `/customers/${customerId}/subscriptions/${subscriptionId}/payments`; } export default class SubscriptionPaymentsBinder extends Binder { diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 4a6af926..4603ec57 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -121,7 +121,11 @@ export default class NetworkClient { const agent = new https.Agent({ ca: caCertificates }); // Create the request function. - this.request = (pathname, options) => fetch(`${apiEndpoint}${pathname}`, { agent, ...options, headers: { ...headers, ...options?.headers } }); + this.request = (pathname, options) => { + // If the pathname starts with a slash, remove it and prepend the API endpoint. + const url = pathname.startsWith('/') ? `${apiEndpoint}${pathname.substring(1)}` : pathname; + return fetch(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); + }; // Make the Axios instance request multiple times in some scenarios. // makeRetrying(this.axiosInstance); From c7787e1d9b355e2e6324056a0595d3e579b732fd Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Tue, 9 Jul 2024 17:23:47 +0200 Subject: [PATCH 03/23] Rewrite tests to use Nock instead of the Axios adapter. What's missing from these changes, is an explicit networkMocker.cleanup(). --- tests/unit/errors.test.ts | 31 +++++++----- tests/unit/models/onboarding.test.ts | 11 +++-- tests/unit/models/order.test.ts | 11 +++-- tests/unit/models/payment.test.ts | 11 +++-- tests/unit/models/profile.test.ts | 11 +++-- tests/unit/models/refund.test.ts | 11 +++-- tests/unit/models/subscription.test.ts | 11 +++-- tests/unit/resources/applePay.test.ts | 11 +++-- tests/unit/resources/chargebacks.test.ts | 11 +++-- tests/unit/resources/customers.test.ts | 38 ++++++++------- .../unit/resources/customers/mandates.test.ts | 29 ++++++----- .../unit/resources/customers/payments.test.ts | 20 ++++---- .../resources/customers/subscriptions.test.ts | 47 ++++++++++-------- tests/unit/resources/onboarding.test.ts | 18 +++---- tests/unit/resources/orders.test.ts | 48 +++++++++++-------- tests/unit/resources/orders/payments.test.ts | 9 ++-- tests/unit/resources/orders/refunds.test.ts | 25 +++++----- tests/unit/resources/orders/shipments.test.ts | 16 ++++--- tests/unit/resources/organizations.test.ts | 16 ++++--- tests/unit/resources/payments.test.ts | 38 ++++++++------- .../unit/resources/payments/captures.test.ts | 18 +++---- tests/unit/resources/payments/refunds.test.ts | 20 ++++---- tests/unit/resources/permissions.test.ts | 20 ++++---- tests/unit/resources/profiles.test.ts | 31 ++++++------ tests/unit/resources/refunds.test.ts | 11 +++-- tests/unit/resources/subscriptions.test.ts | 11 +++-- .../resources/subscriptions/payments.test.ts | 11 +++-- 27 files changed, 303 insertions(+), 242 deletions(-) diff --git a/tests/unit/errors.test.ts b/tests/unit/errors.test.ts index b097685a..47936906 100644 --- a/tests/unit/errors.test.ts +++ b/tests/unit/errors.test.ts @@ -1,20 +1,25 @@ -import { MollieApiError, PaymentCreateParams } from '../..'; -import wireMockClient from '../wireMockClient'; +import { MollieApiError, MollieClient, PaymentCreateParams } from '../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../NetworkMocker'; describe('errorHandling', () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + let mollieClient: MollieClient; + + beforeAll(async () => { + mollieClient = await networkMocker.prepare(); + }); test('data property passthrough', async () => { expect.assertions(6); - adapter.onGet('/customers/cst_chinchilla').reply(404, { + networkMocker.intercept('GET', '/customers/cst_chinchilla', 404, { status: 404, title: 'Not Found', detail: 'No customer exists with token cst_chinchilla.', _links: { documentation: { href: 'https://docs.mollie.com/guides/handling-errors', type: 'text/html' } }, - }); + }).twice(); try { - await bluster(client.customers.get.bind(client.customers))('cst_chinchilla'); + await bluster(mollieClient.customers.get.bind(mollieClient.customers))('cst_chinchilla'); } catch (error) { expect(error).toBeInstanceOf(Error); expect(error.message).toBe('No customer exists with token cst_chinchilla.'); @@ -22,17 +27,17 @@ describe('errorHandling', () => { expect(JSON.parse(JSON.stringify(error)).message).toBe('No customer exists with token cst_chinchilla.'); } - adapter.onPost('/payments').reply(422, { + networkMocker.intercept('POST', '/payments', 422, { status: 422, title: 'Unprocessable Entity', detail: 'The amount is required for payments', field: 'amount', _links: { documentation: { href: 'https://docs.mollie.com/guides/handling-errors', type: 'text/html' } }, - }); + }).twice(); const createPaymentParams = {}; try { - await bluster(client.payments.create.bind(client.payments))(createPaymentParams as PaymentCreateParams); + await bluster(mollieClient.payments.create.bind(mollieClient.payments))(createPaymentParams as PaymentCreateParams); } catch (error) { expect(error).toBeInstanceOf(MollieApiError); expect(error.field).toBe('amount'); @@ -42,19 +47,21 @@ describe('errorHandling', () => { test('idempotency key retention', async () => { expect.assertions(2); - adapter.onPost('/payments').reply(900, { + networkMocker.intercept('POST', '/payments', 900, { status: 900, title: 'Custom failing error', - }); + }).twice(); const createPaymentParams = { idempotencyKey: 'mock-key', }; try { - await bluster(client.payments.create.bind(client.payments))(createPaymentParams as PaymentCreateParams); + await bluster(mollieClient.payments.create.bind(mollieClient.payments))(createPaymentParams as PaymentCreateParams); } catch (error) { expect(error).toBeInstanceOf(MollieApiError); expect(error.idempotencyKey).toBe('mock-key'); } }); + + afterAll(() => networkMocker.cleanup()); }); diff --git a/tests/unit/models/onboarding.test.ts b/tests/unit/models/onboarding.test.ts index aa99b59e..3edce665 100644 --- a/tests/unit/models/onboarding.test.ts +++ b/tests/unit/models/onboarding.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Onboarding } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getOnboarding(status) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/onboarding/me').reply(200, { + networkMocker.intercept('GET', '/onboarding/me', 200, { resource: 'onboarding', name: 'Mollie B.V.', signedUpAt: '2018-12-20T10:49:08+00:00', @@ -29,9 +30,9 @@ async function getOnboarding(status) { type: 'text/html', }, }, - }); + }).twice(); - return await bluster(client.onboarding.get.bind(client.onboarding))(); + return await bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); } test('onboardingStatuses', () => { diff --git a/tests/unit/models/order.test.ts b/tests/unit/models/order.test.ts index 579056e6..bb3f95d8 100644 --- a/tests/unit/models/order.test.ts +++ b/tests/unit/models/order.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Order } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getOrder(status, additionalLinks?: object) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders/ord_pbjz1x').reply(200, { + networkMocker.intercept('GET', '/orders/ord_pbjz1x', 200, { resource: 'order', id: 'ord_pbjz1x', profileId: 'pfl_URR55HPMGx', @@ -130,9 +131,9 @@ async function getOrder(status, additionalLinks?: object) { }, ...additionalLinks, }, - }); + }).twice(); - return await bluster(client.orders.get.bind(client.orders))('ord_pbjz1x'); + return await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz1x'); } // These helper methods are not yet implemented for orders. diff --git a/tests/unit/models/payment.test.ts b/tests/unit/models/payment.test.ts index edc50ca2..059ade70 100644 --- a/tests/unit/models/payment.test.ts +++ b/tests/unit/models/payment.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Payment } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getPayment(status, additionalProperties?: object, additionalLinks?: object) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments/tr_44aKxzEbr8').reply(200, { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { resource: 'payment', id: 'tr_44aKxzEbr8', mode: 'test', @@ -55,9 +56,9 @@ async function getPayment(status, additionalProperties?: object, additionalLinks ...additionalLinks, }, ...additionalProperties, - }); + }).twice(); - return await bluster(client.payments.get.bind(client.payments))('tr_44aKxzEbr8'); + return await bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); } test('paymentStatuses', () => { diff --git a/tests/unit/models/profile.test.ts b/tests/unit/models/profile.test.ts index 7f96dc94..0a12c9e4 100644 --- a/tests/unit/models/profile.test.ts +++ b/tests/unit/models/profile.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Profile } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getProfile(status) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/profiles/pfl_ahe8z8OPut').reply(200, { + networkMocker.intercept('GET', '/profiles/pfl_ahe8z8OPut', 200, { resource: 'profile', id: 'pfl_ahe8z8OPut', mode: 'live', @@ -44,9 +45,9 @@ async function getProfile(status) { type: 'text/html', }, }, - }); + }).twice(); - return await bluster(client.profiles.get.bind(client.profiles))('pfl_ahe8z8OPut'); + return await bluster(mollieClient.profiles.get.bind(mollieClient.profiles))('pfl_ahe8z8OPut'); } test('profileStatuses', () => { diff --git a/tests/unit/models/refund.test.ts b/tests/unit/models/refund.test.ts index b5e84e5b..4ee2a9df 100644 --- a/tests/unit/models/refund.test.ts +++ b/tests/unit/models/refund.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Refund } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getRefund(status) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/refunds').reply(200, { + networkMocker.intercept('GET', '/refunds', 200, { _embedded: { refunds: [ { @@ -48,9 +49,9 @@ async function getRefund(status) { next: null, }, count: 1, - }); + }).twice(); - return await bluster(client.refunds.page.bind(client.refunds))().then(refunds => refunds[0]); + return await bluster(mollieClient.refunds.page.bind(mollieClient.refunds))().then(refunds => refunds[0]); } test('refundStatuses', () => { diff --git a/tests/unit/models/subscription.test.ts b/tests/unit/models/subscription.test.ts index 7c57a0ac..0b385a45 100644 --- a/tests/unit/models/subscription.test.ts +++ b/tests/unit/models/subscription.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { Subscription } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; async function getSubscription(status) { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { resource: 'subscription', id: 'sub_wByQa6efm6', mode: 'test', @@ -34,9 +35,9 @@ async function getSubscription(status) { type: 'text/html', }, }, - }); + }).twice(); - return await bluster(client.customerSubscriptions.get.bind(client.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); + return await bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); } test('subscriptionStatuses', () => { diff --git a/tests/unit/resources/applePay.test.ts b/tests/unit/resources/applePay.test.ts index 61a678a2..409c1b67 100644 --- a/tests/unit/resources/applePay.test.ts +++ b/tests/unit/resources/applePay.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('requestApplePayPaymentSession', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/wallets/applepay/sessions').reply(201, { + networkMocker.intercept('POST', '/wallets/applepay/sessions', 201, { epochTimestamp: 1555507053169, expiresAt: 1555510653169, merchantSessionIdentifier: 'SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24', @@ -12,9 +13,9 @@ test('requestApplePayPaymentSession', async () => { domainName: 'pay.example.org', displayName: "Chuck Norris's Store", signature: '308006092a864886f7...8cc030ad3000000000000', - }); + }).twice(); - const applePaySession = await bluster(client.applePay.requestPaymentSession.bind(client.applePay))({ + const applePaySession = await bluster(mollieClient.applePay.requestPaymentSession.bind(mollieClient.applePay))({ domain: 'pay.mywebshop.com', validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', }); diff --git a/tests/unit/resources/chargebacks.test.ts b/tests/unit/resources/chargebacks.test.ts index 099cf422..05e7e7d1 100644 --- a/tests/unit/resources/chargebacks.test.ts +++ b/tests/unit/resources/chargebacks.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; function testChargeback(chargeback, paymentId, chargebackId, amount) { expect(chargeback.resource).toBe('chargeback'); @@ -28,9 +28,10 @@ function testChargeback(chargeback, paymentId, chargebackId, amount) { } test('listChargebacks', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/chargebacks').reply(200, { + networkMocker.intercept('GET', '/chargebacks', 200, { _embedded: { chargebacks: [ { @@ -102,9 +103,9 @@ test('listChargebacks', async () => { }, }, count: 2, - }); + }).twice(); - const chargebacks = await bluster(client.chargebacks.page.bind(client.chargebacks))(); + const chargebacks = await bluster(mollieClient.chargebacks.page.bind(mollieClient.chargebacks))(); expect(chargebacks.length).toBe(2); diff --git a/tests/unit/resources/customers.test.ts b/tests/unit/resources/customers.test.ts index 01ea07a4..35bd0f15 100644 --- a/tests/unit/resources/customers.test.ts +++ b/tests/unit/resources/customers.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('createCustomer', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/customers').reply(200, { + networkMocker.intercept('POST', '/customers', 200, { resource: 'customer', id: 'cst_FhQJRw4s2n', mode: 'test', @@ -19,9 +20,9 @@ test('createCustomer', async () => { type: 'text/html', }, }, - }); + }).twice(); - const customer = await bluster(client.customers.create.bind(client.customers))({ + const customer = await bluster(mollieClient.customers.create.bind(mollieClient.customers))({ name: 'John Doe', email: 'johndoe@example.org', }); @@ -42,9 +43,10 @@ test('createCustomer', async () => { }); test('getCustomer', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n', 200, { resource: 'customer', id: 'cst_FhQJRw4s2n', mode: 'test', @@ -60,9 +62,9 @@ test('getCustomer', async () => { type: 'text/html', }, }, - }); + }).twice(); - const customer = await bluster(client.customers.get.bind(client.customers))('cst_FhQJRw4s2n'); + const customer = await bluster(mollieClient.customers.get.bind(mollieClient.customers))('cst_FhQJRw4s2n'); expect(customer.resource).toBe('customer'); expect(customer.id).toBe('cst_FhQJRw4s2n'); @@ -80,9 +82,10 @@ test('getCustomer', async () => { }); test('listCustomers', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers').reply(200, { + networkMocker.intercept('GET', '/customers', 200, { _embedded: { customers: [ { @@ -111,9 +114,9 @@ test('listCustomers', async () => { previous: null, next: null, }, - }); + }).twice(); - const customers = await bluster(client.customers.page.bind(client.customers))(); + const customers = await bluster(mollieClient.customers.page.bind(mollieClient.customers))(); expect(customers.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', @@ -132,12 +135,13 @@ test('listCustomers', async () => { }); test('updateCustomer', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); const expectedName = 'Kaas Broodje'; const expectedEmail = 'kaas.broodje@gmail.com'; - adapter.onPatch('/customers/cst_FhQJRw4s2n').reply(200, { + networkMocker.intercept('PATCH', '/customers/cst_FhQJRw4s2n', 200, { resource: 'customer', id: 'cst_FhQJRw4s2n', mode: 'test', @@ -153,9 +157,9 @@ test('updateCustomer', async () => { type: 'text/html', }, }, - }); + }).twice(); - const updatedCustomer = await bluster(client.customers.update.bind(client.customers))('cst_FhQJRw4s2n', { + const updatedCustomer = await bluster(mollieClient.customers.update.bind(mollieClient.customers))('cst_FhQJRw4s2n', { name: expectedName, email: expectedEmail, }); diff --git a/tests/unit/resources/customers/mandates.test.ts b/tests/unit/resources/customers/mandates.test.ts index 52065249..9c300328 100644 --- a/tests/unit/resources/customers/mandates.test.ts +++ b/tests/unit/resources/customers/mandates.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; test('createCustomerMandate', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/customers/cst_FhQJRw4s2n/mandates').reply(200, { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/mandates', 200, { resource: 'mandate', id: 'mdt_AcQl5fdL4h', status: 'valid', @@ -30,9 +31,9 @@ test('createCustomerMandate', async () => { type: 'text/html', }, }, - }); + }).twice(); - const mandate = await bluster(client.customerMandates.create.bind(client.customerMandates))({ + const mandate = await bluster(mollieClient.customerMandates.create.bind(mollieClient.customerMandates))({ customerId: 'cst_FhQJRw4s2n', consumerName: 'John Doe', method: 'directdebit', @@ -55,9 +56,10 @@ test('createCustomerMandate', async () => { }); test('getCustomerMandate', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', 200, { resource: 'mandate', id: 'mdt_AcQl5fdL4h', status: 'valid', @@ -84,9 +86,9 @@ test('getCustomerMandate', async () => { type: 'text/html', }, }, - }); + }).twice(); - const mandate = await bluster(client.customerMandates.get.bind(client.customerMandates))('mdt_AcQl5fdL4h', { customerId: 'cst_FhQJRw4s2n' }); + const mandate = await bluster(mollieClient.customerMandates.get.bind(mollieClient.customerMandates))('mdt_AcQl5fdL4h', { customerId: 'cst_FhQJRw4s2n' }); expect(mandate.resource).toBe('mandate'); expect(mandate.status).toBe('valid'); @@ -104,9 +106,10 @@ test('getCustomerMandate', async () => { }); test('getCustomerMandates', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/mandates').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates', 200, { _embedded: { mandates: [ { @@ -148,9 +151,9 @@ test('getCustomerMandates', async () => { previous: null, next: null, }, - }); + }).twice(); - const mandates = await bluster(client.customerMandates.page.bind(client.customerMandates))({ customerId: 'cst_FhQJRw4s2n' }); + const mandates = await bluster(mollieClient.customerMandates.page.bind(mollieClient.customerMandates))({ customerId: 'cst_FhQJRw4s2n' }); mandates.forEach(mandate => { expect(mandate.resource).toBe('mandate'); diff --git a/tests/unit/resources/customers/payments.test.ts b/tests/unit/resources/customers/payments.test.ts index 003cd87b..530c1f04 100644 --- a/tests/unit/resources/customers/payments.test.ts +++ b/tests/unit/resources/customers/payments.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; test('createCustomerPayment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/customers/cst_FhQJRw4s2n/payments').reply(201, { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/payments', 201, { resource: 'payment', id: 'tr_44aKxzEbr8', mode: 'test', @@ -44,9 +45,9 @@ test('createCustomerPayment', async () => { type: 'text/html', }, }, - }); + }).twice(); - const payment = await bluster(client.customerPayments.create.bind(client.customerPayments))({ + const payment = await bluster(mollieClient.customerPayments.create.bind(mollieClient.customerPayments))({ amount: { currency: 'EUR', value: '20.00', @@ -90,9 +91,10 @@ test('createCustomerPayment', async () => { }); test('listCustomerPayouts', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/payments').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/payments', 200, { _embedded: { payments: [ { @@ -215,9 +217,9 @@ test('listCustomerPayouts', async () => { next: null, }, count: 3, - }); + }).twice(); - const payments = await bluster(client.customerPayments.page.bind(client.customerPayments))({ customerId: 'cst_FhQJRw4s2n' }); + const payments = await bluster(mollieClient.customerPayments.page.bind(mollieClient.customerPayments))({ customerId: 'cst_FhQJRw4s2n' }); expect(payments.length).toBe(3); diff --git a/tests/unit/resources/customers/subscriptions.test.ts b/tests/unit/resources/customers/subscriptions.test.ts index 0a836c69..16801808 100644 --- a/tests/unit/resources/customers/subscriptions.test.ts +++ b/tests/unit/resources/customers/subscriptions.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; test('createCustomerSubscription', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/customers/cst_FhQJRw4s2n/subscriptions').reply(200, { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { resource: 'subscription', id: 'sub_wByQa6efm6', mode: 'test', @@ -33,9 +34,9 @@ test('createCustomerSubscription', async () => { type: 'text/html', }, }, - }); + }).twice(); - const subscription = await bluster(client.customerSubscriptions.create.bind(client.customerSubscriptions))({ + const subscription = await bluster(mollieClient.customerSubscriptions.create.bind(mollieClient.customerSubscriptions))({ customerId: 'cst_FhQJRw4s2n', amount: { value: '10.00', @@ -65,9 +66,10 @@ test('createCustomerSubscription', async () => { }); test('getCustomerSubscription', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { resource: 'subscription', id: 'sub_wByQa6efm6', mode: 'test', @@ -97,9 +99,9 @@ test('getCustomerSubscription', async () => { type: 'text/html', }, }, - }); + }).twice(); - const subscription = await bluster(client.customerSubscriptions.get.bind(client.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); + const subscription = await bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); expect(subscription.resource).toBe('subscription'); expect(subscription.id).toBe('sub_wByQa6efm6'); @@ -121,9 +123,10 @@ test('getCustomerSubscription', async () => { }); test('getCustomerSubscriptions', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_FhQJRw4s2n/subscriptions').reply(200, { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { _embedded: { subscriptions: [ { @@ -168,9 +171,9 @@ test('getCustomerSubscriptions', async () => { previous: null, next: null, }, - }); + }).twice(); - const subscriptions = await bluster(client.customerSubscriptions.page.bind(client.customerSubscriptions))({ customerId: 'cst_FhQJRw4s2n' }); + const subscriptions = await bluster(mollieClient.customerSubscriptions.page.bind(mollieClient.customerSubscriptions))({ customerId: 'cst_FhQJRw4s2n' }); expect(subscriptions.length).toBe(1); @@ -185,9 +188,10 @@ test('getCustomerSubscriptions', async () => { }); test('cancelCustomerSubscription', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onDelete('/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx').reply(200, { + networkMocker.intercept('DELETE', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { resource: 'subscription', id: 'sub_DRjwaT5qHx', mode: 'test', @@ -218,9 +222,9 @@ test('cancelCustomerSubscription', async () => { type: 'text/html', }, }, - }); + }).twice(); - const subscription = await bluster(client.customerSubscriptions.cancel.bind(client.customerSubscriptions))('sub_DRjwaT5qHx', { customerId: 'cst_VhjQebNW5j' }); + const subscription = await bluster(mollieClient.customerSubscriptions.cancel.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { customerId: 'cst_VhjQebNW5j' }); expect(subscription.resource).toBe('subscription'); expect(subscription.id).toBe('sub_DRjwaT5qHx'); @@ -243,13 +247,14 @@ test('cancelCustomerSubscription', async () => { }); test('updateCustomerSubscription', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); const expectedAmountValue = '12.00'; const expectedAmountCurrency = 'EUR'; const expectedStartDate = '2018-12-12'; - adapter.onPatch('/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx').reply(200, { + networkMocker.intercept('PATCH', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { resource: 'subscription', id: 'sub_DRjwaT5qHx', customerId: 'cst_VhjQebNW5j', @@ -280,9 +285,9 @@ test('updateCustomerSubscription', async () => { type: 'text/html', }, }, - }); + }).twice(); - const subscription = await bluster(client.customerSubscriptions.update.bind(client.customerSubscriptions))('sub_DRjwaT5qHx', { + const subscription = await bluster(mollieClient.customerSubscriptions.update.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { customerId: 'cst_VhjQebNW5j', amount: { value: expectedAmountValue, currency: expectedAmountCurrency }, startDate: expectedStartDate, diff --git a/tests/unit/resources/onboarding.test.ts b/tests/unit/resources/onboarding.test.ts index a1eb05f8..bb530dc9 100644 --- a/tests/unit/resources/onboarding.test.ts +++ b/tests/unit/resources/onboarding.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('get', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/onboarding/me').reply(200, { + networkMocker.intercept('GET', '/onboarding/me', 200, { resource: 'onboarding', name: 'Mollie B.V.', signedUpAt: '2018-12-20T10:49:08+00:00', @@ -28,9 +29,9 @@ test('get', async () => { type: 'text/html', }, }, - }); + }).twice(); - const onboarding = await bluster(client.onboarding.get.bind(client.onboarding))(); + const onboarding = await bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); expect(onboarding.resource).toBe('onboarding'); expect(onboarding.name).toBe('Mollie B.V.'); @@ -49,11 +50,12 @@ test('get', async () => { }); test('submit', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/onboarding/me').reply(204); + networkMocker.intercept('POST', '/onboarding/me', 204).twice(); - const result = await bluster(client.onboarding.submit.bind(client.onboarding))(); + const result = await bluster(mollieClient.onboarding.submit.bind(mollieClient.onboarding))(); expect(result).toBe(true); }); diff --git a/tests/unit/resources/orders.test.ts b/tests/unit/resources/orders.test.ts index d152585b..a96e0b44 100644 --- a/tests/unit/resources/orders.test.ts +++ b/tests/unit/resources/orders.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; function composeOrderResponse(orderId, orderStatus = 'created', orderNumber = '1337') { return { @@ -240,11 +240,12 @@ function testOrder(order, orderId, orderStatus = 'created', orderNumber = '1337' } test('createOrder', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/orders').reply(201, composeOrderResponse('ord_pbjz8x')); + networkMocker.intercept('POST', '/orders', 201, composeOrderResponse('ord_pbjz8x')).twice(); - const order = await bluster(client.orders.create.bind(client.orders))({ + const order = await bluster(mollieClient.orders.create.bind(mollieClient.orders))({ amount: { value: '1027.99', currency: 'EUR', @@ -333,19 +334,21 @@ test('createOrder', async () => { }); test('getOrder', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders/ord_pbjz8x').reply(200, composeOrderResponse('ord_pbjz8x')); + networkMocker.intercept('GET', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x')).twice(); - const order = await bluster(client.orders.get.bind(client.orders))('ord_pbjz8x'); + const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz8x'); testOrder(order, 'ord_pbjz8x'); }); test('getOrderIncludingPayments', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders/ord_kEn1PlbGa?embed=payments').reply(200, { + networkMocker.intercept('GET', '/orders/ord_kEn1PlbGa?embed=payments', 200, { resource: 'order', id: 'ord_kEn1PlbGa', profileId: 'pfl_URR55HPMGx', @@ -551,9 +554,9 @@ test('getOrderIncludingPayments', async () => { type: 'text/html', }, }, - }); + }).twice(); - const order = await bluster(client.orders.get.bind(client.orders))('ord_kEn1PlbGa', { embed: ['payments'] }); + const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_kEn1PlbGa', { embed: ['payments'] }); expect(order.id).toBe('ord_kEn1PlbGa'); @@ -589,9 +592,10 @@ test('getOrderIncludingPayments', async () => { }); test('listOrders', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders').reply(200, { + networkMocker.intercept('GET', '/orders', 200, { count: 3, _embedded: { orders: [composeOrderResponse('ord_pbjz1x'), composeOrderResponse('ord_pbjz2y'), composeOrderResponse('ord_pbjz3z')], @@ -611,9 +615,9 @@ test('listOrders', async () => { type: 'text/html', }, }, - }); + }).twice(); - const orders = await client.orders.page(); + const orders = await mollieClient.orders.page(); expect(orders.length).toBe(3); @@ -637,21 +641,23 @@ test('listOrders', async () => { }); test('cancelOrder', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onDelete('/orders/ord_pbjz1x').reply(200, composeOrderResponse('ord_pbjz1x', 'canceled')); + networkMocker.intercept('DELETE', '/orders/ord_pbjz1x', 200, composeOrderResponse('ord_pbjz1x', 'canceled')).twice(); - const order = await bluster(client.orders.cancel.bind(client.orders))('ord_pbjz1x'); + const order = await bluster(mollieClient.orders.cancel.bind(mollieClient.orders))('ord_pbjz1x'); testOrder(order, 'ord_pbjz1x', 'canceled'); }); test('updateOrder', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPatch('/orders/ord_pbjz8x').reply(200, composeOrderResponse('ord_pbjz8x', 'created', '16738')); + networkMocker.intercept('PATCH', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x', 'created', '16738')).twice(); - const order = await bluster(client.orders.update.bind(client.orders))('ord_pbjz8x', { + const order = await bluster(mollieClient.orders.update.bind(mollieClient.orders))('ord_pbjz8x', { orderNumber: '16738', billingAddress: { organizationName: 'Organization Name LTD.', diff --git a/tests/unit/resources/orders/payments.test.ts b/tests/unit/resources/orders/payments.test.ts index 7dc86314..babe2c47 100644 --- a/tests/unit/resources/orders/payments.test.ts +++ b/tests/unit/resources/orders/payments.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; function composePaymentResponse(paymentId, orderId) { return { @@ -58,11 +58,12 @@ function testPayment(payment, paymentId, paymentStatus = 'open') { } test('createOrderPayment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/orders/ord_stTC2WHAuS/payments').reply(201, composePaymentResponse('tr_WDqYK6vllg', 'ord_stTC2WHAuS')); + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/payments', 201, composePaymentResponse('tr_WDqYK6vllg', 'ord_stTC2WHAuS')).twice(); - const payment = await bluster(client.orderPayments.create.bind(client.orderPayments))({ + const payment = await bluster(mollieClient.orderPayments.create.bind(mollieClient.orderPayments))({ orderId: 'ord_stTC2WHAuS', method: 'banktransfer', }); diff --git a/tests/unit/resources/orders/refunds.test.ts b/tests/unit/resources/orders/refunds.test.ts index b0fcef38..783f63e6 100644 --- a/tests/unit/resources/orders/refunds.test.ts +++ b/tests/unit/resources/orders/refunds.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; function composeRefundResponse(refundId, orderId) { return { @@ -88,29 +88,32 @@ function testRefund(refund, refundId, refundStatus = 'pending') { } test('createPartialOrderRefund', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/orders/ord_stTC2WHAuS/refunds').reply(201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')); + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); - const refund = await bluster(client.orderRefunds.create.bind(client.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [{ id: 'odl_dgtxyl', quantity: 1 }] }); + const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [{ id: 'odl_dgtxyl', quantity: 1 }] }); testRefund(refund, 're_4qqhO89gsT'); }); test('createCompleteOrderRefund', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/orders/ord_stTC2WHAuS/refunds').reply(201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')); + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); - const refund = await bluster(client.orderRefunds.create.bind(client.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [] }); + const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [] }); testRefund(refund, 're_4qqhO89gsT'); }); test('listOrderRefunds', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders/ord_stTC2WHAuS/refunds').reply(200, { + networkMocker.intercept('GET', '/orders/ord_stTC2WHAuS/refunds', 200, { count: 1, _embedded: { refunds: [ @@ -194,9 +197,9 @@ test('listOrderRefunds', async () => { type: 'text/html', }, }, - }); + }).twice(); - const refunds = await bluster(client.orderRefunds.page.bind(client.orderRefunds))({ orderId: 'ord_stTC2WHAuS' }); + const refunds = await bluster(mollieClient.orderRefunds.page.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS' }); expect(refunds.length).toBe(1); diff --git a/tests/unit/resources/orders/shipments.test.ts b/tests/unit/resources/orders/shipments.test.ts index 228c04cd..67fc2fa7 100644 --- a/tests/unit/resources/orders/shipments.test.ts +++ b/tests/unit/resources/orders/shipments.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; function composeShipmentResponse(shipmentId, orderId, orderlineStatus = 'shipping') { return { @@ -136,11 +136,12 @@ function testShipment(shipment, shipmentId, orderId) { } test('createShipment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/orders/ord_pbjz8x/shipments').reply(201, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')); + networkMocker.intercept('POST', '/orders/ord_pbjz8x/shipments', 201, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); - const shipment = await bluster(client.orderShipments.create.bind(client.orderShipments))({ + const shipment = await bluster(mollieClient.orderShipments.create.bind(mollieClient.orderShipments))({ orderId: 'ord_pbjz8x', lines: [ { @@ -155,11 +156,12 @@ test('createShipment', async () => { }); test('getShipment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/orders/ord_pbjz8x/shipments/shp_3wmsgCJN4U').reply(200, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')); + networkMocker.intercept('GET', '/orders/ord_pbjz8x/shipments/shp_3wmsgCJN4U', 200, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); - const shipment = await bluster(client.orderShipments.get.bind(client.orderShipments))('shp_3wmsgCJN4U', { orderId: 'ord_pbjz8x' }); + const shipment = await bluster(mollieClient.orderShipments.get.bind(mollieClient.orderShipments))('shp_3wmsgCJN4U', { orderId: 'ord_pbjz8x' }); testShipment(shipment, 'shp_3wmsgCJN4U', 'ord_pbjz8x'); }); diff --git a/tests/unit/resources/organizations.test.ts b/tests/unit/resources/organizations.test.ts index 4660d115..f3279b78 100644 --- a/tests/unit/resources/organizations.test.ts +++ b/tests/unit/resources/organizations.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; const response = { resource: 'organization', @@ -50,21 +50,23 @@ function testOrganization(organization) { } test('getOrganization', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/organizations/org_12345678').reply(200, response); + networkMocker.intercept('GET', '/organizations/org_12345678', 200, response).twice(); - const organization = await bluster(client.organizations.get.bind(client.organizations))('org_12345678'); + const organization = await bluster(mollieClient.organizations.get.bind(mollieClient.organizations))('org_12345678'); testOrganization(organization); }); test('getCurrentOrganization', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/organizations/me').reply(200, response); + networkMocker.intercept('GET', '/organizations/me', 200, response).twice(); - const organization = await bluster(client.organizations.getCurrent.bind(client.organizations))(); + const organization = await bluster(mollieClient.organizations.getCurrent.bind(mollieClient.organizations))(); testOrganization(organization); }); diff --git a/tests/unit/resources/payments.test.ts b/tests/unit/resources/payments.test.ts index ee0ef5a3..b26f09e4 100644 --- a/tests/unit/resources/payments.test.ts +++ b/tests/unit/resources/payments.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('createPayment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/payments').reply(201, { + networkMocker.intercept('POST', '/payments', 201, { resource: 'payment', id: 'tr_44aKxzEbr8', mode: 'test', @@ -40,9 +41,9 @@ test('createPayment', async () => { type: 'text/html', }, }, - }); + }).twice(); - const payment = await bluster(client.payments.create.bind(client.payments))({ + const payment = await bluster(mollieClient.payments.create.bind(mollieClient.payments))({ amount: { currency: 'EUR', value: '20.00', @@ -83,9 +84,10 @@ test('createPayment', async () => { }); test('updatePayment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPatch('payments/tr_7UhSN1zuXS').reply(200, { + networkMocker.intercept('PATCH', '/payments/tr_7UhSN1zuXS', 200, { resource: 'payment', id: 'tr_7UhSN1zuXS', mode: 'test', @@ -121,9 +123,9 @@ test('updatePayment', async () => { type: 'text/html', }, }, - }); + }).twice(); - const payment = await bluster(client.payments.update).bind(client.payments)('tr_7UhSN1zuXS', { + const payment = await bluster(mollieClient.payments.update).bind(mollieClient.payments)('tr_7UhSN1zuXS', { description: 'Order #98765', redirectUrl: 'https://example.org/webshop/order/98765/', webhookUrl: 'https://example.org/webshop/payments/webhook/', @@ -157,9 +159,10 @@ test('updatePayment', async () => { }); test('getPayment', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments/tr_44aKxzEbr8').reply(200, { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { resource: 'payment', id: 'tr_44aKxzEbr8', mode: 'test', @@ -209,9 +212,9 @@ test('getPayment', async () => { type: 'text/html', }, }, - }); + }).twice(); - const payment = await bluster(client.payments.get.bind(client.payments))('tr_44aKxzEbr8'); + const payment = await bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); expect(payment.id).toBe('tr_44aKxzEbr8'); expect(payment.mode).toBe('test'); @@ -239,9 +242,10 @@ test('getPayment', async () => { }); test('listPayments', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments?limit=3').reply(200, { + networkMocker.intercept('GET', '/payments?limit=3', 200, { _embedded: { payments: [ { @@ -355,9 +359,9 @@ test('listPayments', async () => { }, }, count: 3, - }); + }).twice(); - const payments = await bluster(client.payments.page.bind(client.payments))({ limit: 3 }); + const payments = await bluster(mollieClient.payments.page.bind(mollieClient.payments))({ limit: 3 }); expect(payments.length).toBe(3); diff --git a/tests/unit/resources/payments/captures.test.ts b/tests/unit/resources/payments/captures.test.ts index 0b36eef1..81e3658d 100644 --- a/tests/unit/resources/payments/captures.test.ts +++ b/tests/unit/resources/payments/captures.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; function composeCaptureResponse(paymentId = 'tr_WDqYK6vllg', captureId = 'cpt_4qqhO89gsT') { return { @@ -82,19 +82,21 @@ function testCapture(capture) { } test('getCapture', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT').reply(200, composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')); + networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT', 200, composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')).twice(); - const capture = await bluster(client.paymentCaptures.get.bind(client.paymentCaptures))('cpt_4qqhO89gsT', { paymentId: 'tr_WDqYK6vllg' }); + const capture = await bluster(mollieClient.paymentCaptures.get.bind(mollieClient.paymentCaptures))('cpt_4qqhO89gsT', { paymentId: 'tr_WDqYK6vllg' }); testCapture(capture); }); test('listCaptures', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments/tr_WDqYK6vllg/captures').reply(200, { + networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures', 200, { _embedded: { captures: [composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')], }, @@ -111,9 +113,9 @@ test('listCaptures', async () => { previous: null, next: null, }, - }); + }).twice(); - const captures = await bluster(client.paymentCaptures.page.bind(client.paymentCaptures))({ paymentId: 'tr_WDqYK6vllg' }); + const captures = await bluster(mollieClient.paymentCaptures.page.bind(mollieClient.paymentCaptures))({ paymentId: 'tr_WDqYK6vllg' }); expect(captures.length).toBe(1); diff --git a/tests/unit/resources/payments/refunds.test.ts b/tests/unit/resources/payments/refunds.test.ts index a28e8231..315b0034 100644 --- a/tests/unit/resources/payments/refunds.test.ts +++ b/tests/unit/resources/payments/refunds.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; test('getRefund', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/payments/tr_44aKxzEbr8/refunds/re_PsAvxvLsnm').reply(200, { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8/refunds/re_PsAvxvLsnm', 200, { resource: 'refund', id: 're_PsAvxvLsnm', amount: { @@ -32,9 +33,9 @@ test('getRefund', async () => { type: 'text/html', }, }, - }); + }).twice(); - const refund = await bluster(client.paymentRefunds.get.bind(client.paymentRefunds))('re_PsAvxvLsnm', { paymentId: 'tr_44aKxzEbr8' }); + const refund = await bluster(mollieClient.paymentRefunds.get.bind(mollieClient.paymentRefunds))('re_PsAvxvLsnm', { paymentId: 'tr_44aKxzEbr8' }); expect(refund.id).toBe('re_PsAvxvLsnm'); @@ -70,9 +71,10 @@ test('getRefund', async () => { }); test('createRefund', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onPost('/payments/tr_44aKxzEbr8/refunds').reply(201, { + networkMocker.intercept('POST', '/payments/tr_44aKxzEbr8/refunds', 201, { resource: 'refund', id: 're_PsAvxvLsnm', amount: { @@ -101,9 +103,9 @@ test('createRefund', async () => { type: 'text/html', }, }, - }); + }).twice(); - const refund = await bluster(client.paymentRefunds.create.bind(client.paymentRefunds))({ paymentId: 'tr_44aKxzEbr8', amount: { currency: 'EUR', value: '20.00' } }); + const refund = await bluster(mollieClient.paymentRefunds.create.bind(mollieClient.paymentRefunds))({ paymentId: 'tr_44aKxzEbr8', amount: { currency: 'EUR', value: '20.00' } }); expect(refund.id).toBe('re_PsAvxvLsnm'); diff --git a/tests/unit/resources/permissions.test.ts b/tests/unit/resources/permissions.test.ts index db9781ba..e2c4bfdc 100644 --- a/tests/unit/resources/permissions.test.ts +++ b/tests/unit/resources/permissions.test.ts @@ -1,4 +1,4 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; function testPermission(permission, id) { expect(permission.resource).toBe('permission'); @@ -12,7 +12,8 @@ function testPermission(permission, id) { } test('getPermissions', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); await Promise.all( [ @@ -37,7 +38,7 @@ test('getPermissions', async () => { 'organizations.read', 'organizations.write', ].map(async id => { - adapter.onGet(`/permissions/${id}`).reply(200, { + networkMocker.intercept('GET', `/permissions/${id}`, 200, { resource: 'permission', id, description: 'Some dummy permission description', @@ -52,9 +53,9 @@ test('getPermissions', async () => { type: 'text/html', }, }, - }); + }).twice(); - const permission = await bluster(client.permissions.get.bind(client.permissions))(id); + const permission = await bluster(mollieClient.permissions.get.bind(mollieClient.permissions))(id); testPermission(permission, id); }), @@ -62,9 +63,10 @@ test('getPermissions', async () => { }); test('listPermissions', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/permissions').reply(200, { + networkMocker.intercept('GET', '/permissions', 200, { _embedded: { permissions: [ { @@ -112,9 +114,9 @@ test('listPermissions', async () => { type: 'application/hal+json', }, }, - }); + }).twice(); - const permissions = await bluster(client.permissions.list.bind(client.permissions))(); + const permissions = await bluster(mollieClient.permissions.list.bind(mollieClient.permissions))(); expect(permissions.length).toBe(2); diff --git a/tests/unit/resources/profiles.test.ts b/tests/unit/resources/profiles.test.ts index f2619439..0ad64728 100644 --- a/tests/unit/resources/profiles.test.ts +++ b/tests/unit/resources/profiles.test.ts @@ -1,16 +1,17 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('getProfile', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); await Promise.all( ( [ - ['/profiles/pfl_ahe8z8OPut', bluster(client.profiles.get.bind(client.profiles)).bind(undefined, 'pfl_ahe8z8OPut')], - ['/profiles/me', bluster(client.profiles.getCurrent.bind(client.profiles))], + ['/profiles/pfl_ahe8z8OPut', bluster(mollieClient.profiles.get.bind(mollieClient.profiles)).bind(undefined, 'pfl_ahe8z8OPut')], + ['/profiles/me', bluster(mollieClient.profiles.getCurrent.bind(mollieClient.profiles))], ] as [string, () => Promise][] ).map(async ([url, get]) => { - adapter.onGet(url).reply(200, { + networkMocker.intercept('GET', url, 200, { resource: 'profile', id: 'pfl_ahe8z8OPut', mode: 'live', @@ -50,7 +51,7 @@ test('getProfile', async () => { type: 'text/html', }, }, - }); + }).twice(); const profile = await get(); @@ -80,9 +81,10 @@ test('getProfile', async () => { }); test('listProfiles', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/profiles').reply(200, { + networkMocker.intercept('GET', '/profiles', 200, { _embedded: { profiles: [ { @@ -182,9 +184,9 @@ test('listProfiles', async () => { previous: null, next: null, }, - }); + }).twice(); - const profiles = await bluster(client.profiles.page.bind(client.profiles))(); + const profiles = await bluster(mollieClient.profiles.page.bind(mollieClient.profiles))(); expect(profiles.length).toBe(2); @@ -194,13 +196,14 @@ test('listProfiles', async () => { }); test('updateProfile', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); const expectedWebsiteName = 'Mollie'; const expectedEmail = 'mollie@mollie.com'; const expectedPhone = '31123456766'; - adapter.onPatch('/profiles/pfl_ahe8z8OPut').reply(200, { + networkMocker.intercept('PATCH', '/profiles/pfl_ahe8z8OPut', 200, { resource: 'profile', id: 'pfl_ahe8z8OPut', mode: 'live', @@ -240,9 +243,9 @@ test('updateProfile', async () => { type: 'text/html', }, }, - }); + }).twice(); - const profile = await bluster(client.profiles.update.bind(client.profiles))('pfl_ahe8z8OPut', { name: expectedWebsiteName, email: expectedEmail, phone: expectedPhone }); + const profile = await bluster(mollieClient.profiles.update.bind(mollieClient.profiles))('pfl_ahe8z8OPut', { name: expectedWebsiteName, email: expectedEmail, phone: expectedPhone }); expect(profile.name).toBe(expectedWebsiteName); expect(profile.email).toBe(expectedEmail); diff --git a/tests/unit/resources/refunds.test.ts b/tests/unit/resources/refunds.test.ts index 8bffdb29..825a042e 100644 --- a/tests/unit/resources/refunds.test.ts +++ b/tests/unit/resources/refunds.test.ts @@ -1,10 +1,11 @@ -import wireMockClient from '../../wireMockClient'; import { List, Refund } from '../../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('listRefunds', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/refunds').reply(200, { + networkMocker.intercept('GET', '/refunds', 200, { _embedded: { refunds: [ { @@ -48,9 +49,9 @@ test('listRefunds', async () => { next: null, }, count: 1, - }); + }).twice(); - const refunds: List = await bluster(client.refunds.page.bind(client.refunds))(); + const refunds: List = await bluster(mollieClient.refunds.page.bind(mollieClient.refunds))(); expect(refunds.length).toBe(1); diff --git a/tests/unit/resources/subscriptions.test.ts b/tests/unit/resources/subscriptions.test.ts index a16fd014..e6c9218a 100644 --- a/tests/unit/resources/subscriptions.test.ts +++ b/tests/unit/resources/subscriptions.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; test('listPageOfRootSubscriptions', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/subscriptions').reply(200, { + networkMocker.intercept('GET', '/subscriptions', 200, { _embedded: { subscriptions: [ { @@ -48,9 +49,9 @@ test('listPageOfRootSubscriptions', async () => { previous: null, next: null, }, - }); + }).twice(); - const subscriptions = await bluster(client.subscription.page.bind(client.subscription))(); + const subscriptions = await bluster(mollieClient.subscription.page.bind(mollieClient.subscription))(); expect(subscriptions.length).toBe(1); diff --git a/tests/unit/resources/subscriptions/payments.test.ts b/tests/unit/resources/subscriptions/payments.test.ts index 231106ba..7420117d 100644 --- a/tests/unit/resources/subscriptions/payments.test.ts +++ b/tests/unit/resources/subscriptions/payments.test.ts @@ -1,9 +1,10 @@ -import wireMockClient from '../../../wireMockClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; test('listSubscriptionPayments', async () => { - const { adapter, client } = wireMockClient(); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); + const mollieClient = await networkMocker.prepare(); - adapter.onGet('/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments').reply(200, { + networkMocker.intercept('GET', '/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments', 200, { _embedded: { payments: [ { @@ -135,9 +136,9 @@ test('listSubscriptionPayments', async () => { next: null, }, count: 2, - }); + }).twice(); - const payments = await bluster(client.subscriptionPayments.page.bind(client.subscriptionPayments))({ customerId: 'cst_8wmqcHMN4U', subscriptionId: 'sub_8JfGzs6v3K' }); + const payments = await bluster(mollieClient.subscriptionPayments.page.bind(mollieClient.subscriptionPayments))({ customerId: 'cst_8wmqcHMN4U', subscriptionId: 'sub_8JfGzs6v3K' }); expect(payments.length).toBe(2); From 2b85fa567e664e360b1307aac7d6b3f119d4fe23 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Wed, 10 Jul 2024 11:39:24 +0200 Subject: [PATCH 04/23] Add Nock restoration. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests now call networkMocker.cleanup() ‒ which in turn calls nock.restore() ‒ to ensure tests don't affect each other. --- tests/NetworkMocker.ts | 10 + tests/profiles/profiles.test.ts | 45 +- tests/unit/models/onboarding.test.ts | 59 +- tests/unit/models/order.test.ts | 251 +++--- tests/unit/models/payment.test.ts | 109 ++- tests/unit/models/profile.test.ts | 87 +- tests/unit/models/refund.test.ts | 89 +- tests/unit/models/subscription.test.ts | 67 +- tests/unit/resources/applePay.test.ts | 49 +- tests/unit/resources/chargebacks.test.ts | 157 ++-- tests/unit/resources/customers.test.ts | 284 ++++--- .../unit/resources/customers/mandates.test.ts | 273 +++--- .../unit/resources/customers/payments.test.ts | 404 +++++---- .../resources/customers/subscriptions.test.ts | 511 ++++++------ tests/unit/resources/onboarding.test.ts | 92 +- tests/unit/resources/orders.test.ts | 784 +++++++++--------- tests/unit/resources/orders/payments.test.ts | 17 +- tests/unit/resources/orders/refunds.test.ts | 193 +++-- tests/unit/resources/orders/shipments.test.ts | 42 +- tests/unit/resources/organizations.test.ts | 26 +- tests/unit/resources/payments.test.ts | 648 +++++++-------- .../unit/resources/payments/captures.test.ts | 78 +- tests/unit/resources/payments/refunds.test.ts | 234 +++--- tests/unit/resources/permissions.test.ts | 180 ++-- tests/unit/resources/profiles.test.ts | 385 +++++---- tests/unit/resources/refunds.test.ts | 121 ++- tests/unit/resources/subscriptions.test.ts | 99 ++- .../resources/subscriptions/payments.test.ts | 267 +++--- 28 files changed, 2757 insertions(+), 2804 deletions(-) diff --git a/tests/NetworkMocker.ts b/tests/NetworkMocker.ts index 62cbcbbc..01e9c1ca 100644 --- a/tests/NetworkMocker.ts +++ b/tests/NetworkMocker.ts @@ -100,6 +100,11 @@ class NetworkMocker extends BaseNetworkMocker { }, ); } + + // Consider using Explicit Resource Management (https://github.com/tc39/proposal-explicit-resource-management). + use(user: (usables: [mollieClient: MollieClient, networkMocker: NetworkMocker]) => MaybePromise) { + return this.prepare().then(mollieClient => user([mollieClient, this])).finally(this.cleanup); + } } /** @@ -125,6 +130,11 @@ class AutomaticNetworkMocker extends BaseNetworkMocker { }); return client; } + + // Consider using Explicit Resource Management (https://github.com/tc39/proposal-explicit-resource-management). + use(user: (mollieClient: MollieClient) => MaybePromise) { + return this.prepare().then(user).finally(this.cleanup); + } } export default apply(NetworkMocker as typeof NetworkMocker & { Auto: typeof AutomaticNetworkMocker }, NetworkMocker => (NetworkMocker.Auto = AutomaticNetworkMocker)); diff --git a/tests/profiles/profiles.test.ts b/tests/profiles/profiles.test.ts index a0bc574d..f636203b 100644 --- a/tests/profiles/profiles.test.ts +++ b/tests/profiles/profiles.test.ts @@ -5,29 +5,26 @@ import NetworkMocker, { getAccessTokenClientProvider } from '../NetworkMocker'; // 'replay' ‒ This test uses existing recordings to simulate the network. const networkMode = 'replay'; -test('profiles', async () => { - const networkMocker = new NetworkMocker.Auto(networkMode, getAccessTokenClientProvider, 'profiles'); - const mollieClient = await networkMocker.prepare(); - - // Create the profile. - let profile = await mollieClient.profiles.create({ - name: 'Iteration test profile', - email: 'example@example.org', - phone: '+31208202070', - website: 'https://example.org', - mode: ApiMode.test, +test('profiles', () => { + return new NetworkMocker.Auto(networkMode, getAccessTokenClientProvider, 'profiles').use(async mollieClient => { + // Create the profile. + let profile = await mollieClient.profiles.create({ + name: 'Iteration test profile', + email: 'example@example.org', + phone: '+31208202070', + website: 'https://example.org', + mode: ApiMode.test, + }); + // Update the profile. + profile = await mollieClient.profiles.update(profile.id, { businessCategory: 'RESTAURANTS_NIGHTLIFE' }); + expect(profile.businessCategory).toBe('RESTAURANTS_NIGHTLIFE'); + // Enable and disable giftcard issuer. + const giftcardIssuer = await mollieClient.profileGiftcardIssuers.enable({ profileId: profile.id, id: 'festivalcadeau' }); + await mollieClient.profileGiftcardIssuers.disable({ profileId: profile.id, id: giftcardIssuer.id }); + // Enable and disable voucher issuer. + const voucherIssuer = await mollieClient.profileVoucherIssuers.enable({ profileId: profile.id, id: 'appetiz', contractId: 'test_contract_id' }); + await mollieClient.profileVoucherIssuers.disable({ profileId: profile.id, id: voucherIssuer.id }); + // Delete the profile. + expect(await mollieClient.profiles.delete(profile.id)).toBe(true); }); - // Update the profile. - profile = await mollieClient.profiles.update(profile.id, { businessCategory: 'RESTAURANTS_NIGHTLIFE' }); - expect(profile.businessCategory).toBe('RESTAURANTS_NIGHTLIFE'); - // Enable and disable giftcard issuer. - const giftcardIssuer = await mollieClient.profileGiftcardIssuers.enable({ profileId: profile.id, id: 'festivalcadeau' }); - await mollieClient.profileGiftcardIssuers.disable({ profileId: profile.id, id: giftcardIssuer.id }); - // Enable and disable voucher issuer. - const voucherIssuer = await mollieClient.profileVoucherIssuers.enable({ profileId: profile.id, id: 'appetiz', contractId: 'test_contract_id' }); - await mollieClient.profileVoucherIssuers.disable({ profileId: profile.id, id: voucherIssuer.id }); - // Delete the profile. - expect(await mollieClient.profiles.delete(profile.id)).toBe(true); - - networkMocker.cleanup(); }); diff --git a/tests/unit/models/onboarding.test.ts b/tests/unit/models/onboarding.test.ts index 3edce665..b6817711 100644 --- a/tests/unit/models/onboarding.test.ts +++ b/tests/unit/models/onboarding.test.ts @@ -1,38 +1,37 @@ import { Onboarding } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getOnboarding(status) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/onboarding/me', 200, { - resource: 'onboarding', - name: 'Mollie B.V.', - signedUpAt: '2018-12-20T10:49:08+00:00', - status, - canReceivePayments: true, - canReceiveSettlements: true, - _links: { - self: { - href: 'https://api.mollie.com/v2/onboarding/me', - type: 'application/hal+json', - }, - onboarding: { - href: 'https://www.mollie.com/dashboard/onboarding', - type: 'text/html', - }, - organization: { - href: 'https://api.mollie.com/v2/organization/org_12345', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', - type: 'text/html', +function getOnboarding(status) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/onboarding/me', 200, { + resource: 'onboarding', + name: 'Mollie B.V.', + signedUpAt: '2018-12-20T10:49:08+00:00', + status, + canReceivePayments: true, + canReceiveSettlements: true, + _links: { + self: { + href: 'https://api.mollie.com/v2/onboarding/me', + type: 'application/hal+json', + }, + onboarding: { + href: 'https://www.mollie.com/dashboard/onboarding', + type: 'text/html', + }, + organization: { + href: 'https://api.mollie.com/v2/organization/org_12345', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - return await bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); + return bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); + }); } test('onboardingStatuses', () => { diff --git a/tests/unit/models/order.test.ts b/tests/unit/models/order.test.ts index bb3f95d8..05a4b146 100644 --- a/tests/unit/models/order.test.ts +++ b/tests/unit/models/order.test.ts @@ -1,139 +1,138 @@ import { Order } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getOrder(status, additionalLinks?: object) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/orders/ord_pbjz1x', 200, { - resource: 'order', - id: 'ord_pbjz1x', - profileId: 'pfl_URR55HPMGx', - amount: { - value: '1027.99', - currency: 'EUR', - }, - amountCaptured: { - value: '0.00', - currency: 'EUR', - }, - amountRefunded: { - value: '0.00', - currency: 'EUR', - }, - status, - metadata: { - order_id: '1337', - description: 'Lego cars', - }, - consumerDateOfBirth: '1958-01-31', - createdAt: '2018-08-02T09:29:56+00:00', - mode: 'live', - billingAddress: { - organizationName: 'Organization Name LTD.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - shippingAddress: { - organizationName: 'Organization Name LTD.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - orderNumber: '1337', - locale: 'nl_NL', - method: 'klarnapaylater', - isCancelable: true, - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - lines: [ - { - resource: 'orderline', - id: 'odl_dgtxyl', - orderId: '1337', - name: 'LEGO 42083 Bugatti Chiron', - productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', - imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', - sku: '5702016116977', - type: 'physical', - status: 'created', - isCancelable: true, - quantity: 2, - unitPrice: { - value: '399.00', - currency: 'EUR', - }, - vatRate: '21.00', - vatAmount: { - value: '121.14', - currency: 'EUR', - }, - discountAmount: { - value: '100.00', - currency: 'EUR', +function getOrder(status, additionalLinks?: object) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders/ord_pbjz1x', 200, { + resource: 'order', + id: 'ord_pbjz1x', + profileId: 'pfl_URR55HPMGx', + amount: { + value: '1027.99', + currency: 'EUR', + }, + amountCaptured: { + value: '0.00', + currency: 'EUR', + }, + amountRefunded: { + value: '0.00', + currency: 'EUR', + }, + status, + metadata: { + order_id: '1337', + description: 'Lego cars', + }, + consumerDateOfBirth: '1958-01-31', + createdAt: '2018-08-02T09:29:56+00:00', + mode: 'live', + billingAddress: { + organizationName: 'Organization Name LTD.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + shippingAddress: { + organizationName: 'Organization Name LTD.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + orderNumber: '1337', + locale: 'nl_NL', + method: 'klarnapaylater', + isCancelable: true, + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + lines: [ + { + resource: 'orderline', + id: 'odl_dgtxyl', + orderId: '1337', + name: 'LEGO 42083 Bugatti Chiron', + productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', + imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', + sku: '5702016116977', + type: 'physical', + status: 'created', + isCancelable: true, + quantity: 2, + unitPrice: { + value: '399.00', + currency: 'EUR', + }, + vatRate: '21.00', + vatAmount: { + value: '121.14', + currency: 'EUR', + }, + discountAmount: { + value: '100.00', + currency: 'EUR', + }, + totalAmount: { + value: '698.00', + currency: 'EUR', + }, + createdAt: '2018-08-02T09:29:56+00:00', }, - totalAmount: { - value: '698.00', - currency: 'EUR', + { + resource: 'orderline', + id: 'odl_jp31jz', + orderId: '1337', + name: 'LEGO 42056 Porsche 911 GT3 RS', + productUrl: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', + imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', + sku: '5702015594028', + type: 'digital', + status: 'created', + isCancelable: true, + quantity: 1, + unitPrice: { + value: '329.99', + currency: 'EUR', + }, + vatRate: '21.00', + vatAmount: { + value: '57.27', + currency: 'EUR', + }, + totalAmount: { + value: '329.99', + currency: 'EUR', + }, + createdAt: '2018-08-02T09:29:56+00:00', }, - createdAt: '2018-08-02T09:29:56+00:00', - }, - { - resource: 'orderline', - id: 'odl_jp31jz', - orderId: '1337', - name: 'LEGO 42056 Porsche 911 GT3 RS', - productUrl: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', - imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', - sku: '5702015594028', - type: 'digital', - status: 'created', - isCancelable: true, - quantity: 1, - unitPrice: { - value: '329.99', - currency: 'EUR', + ], + _links: { + self: { + href: `https://api.mollie.com/v2/orders/1337`, + type: 'application/hal+json', }, - vatRate: '21.00', - vatAmount: { - value: '57.27', - currency: 'EUR', + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', + type: 'text/html', }, - totalAmount: { - value: '329.99', - currency: 'EUR', + documentation: { + href: 'https://docs.mollie.com/reference/v2/orders-api/get-order', + type: 'text/html', }, - createdAt: '2018-08-02T09:29:56+00:00', + ...additionalLinks, }, - ], - _links: { - self: { - href: `https://api.mollie.com/v2/orders/1337`, - type: 'application/hal+json', - }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', - type: 'text/html', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/orders-api/get-order', - type: 'text/html', - }, - ...additionalLinks, - }, - }).twice(); + }).twice(); - return await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz1x'); + return bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz1x'); + }); } // These helper methods are not yet implemented for orders. diff --git a/tests/unit/models/payment.test.ts b/tests/unit/models/payment.test.ts index 059ade70..386be241 100644 --- a/tests/unit/models/payment.test.ts +++ b/tests/unit/models/payment.test.ts @@ -1,64 +1,63 @@ import { Payment } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getPayment(status, additionalProperties?: object, additionalLinks?: object) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { - resource: 'payment', - id: 'tr_44aKxzEbr8', - mode: 'test', - createdAt: '2018-03-13T14:02:29+00:00', - amount: { - value: '20.00', - currency: 'EUR', - }, - description: 'My first API payment', - method: 'ideal', - metadata: { - order_id: '1234', - }, - status, - amountRefunded: { - value: '0.00', - currency: 'EUR', - }, - amountRemaining: { - value: '20.00', - currency: 'EUR', - }, - details: { - consumerName: 'T. TEST', - consumerAccount: 'NL17RABO0213698412', - consumerBic: 'TESTNL99', - }, - locale: 'nl_NL', - countryCode: 'NL', - profileId: 'pfl_2A1gacu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - settlementAmount: { - value: '20.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', +function getPayment(status, additionalProperties?: object, additionalLinks?: object) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { + resource: 'payment', + id: 'tr_44aKxzEbr8', + mode: 'test', + createdAt: '2018-03-13T14:02:29+00:00', + amount: { + value: '20.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', - type: 'text/html', + description: 'My first API payment', + method: 'ideal', + metadata: { + order_id: '1234', }, - ...additionalLinks, - }, - ...additionalProperties, - }).twice(); + status, + amountRefunded: { + value: '0.00', + currency: 'EUR', + }, + amountRemaining: { + value: '20.00', + currency: 'EUR', + }, + details: { + consumerName: 'T. TEST', + consumerAccount: 'NL17RABO0213698412', + consumerBic: 'TESTNL99', + }, + locale: 'nl_NL', + countryCode: 'NL', + profileId: 'pfl_2A1gacu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + settlementAmount: { + value: '20.00', + currency: 'EUR', + }, + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', + type: 'text/html', + }, + ...additionalLinks, + }, + ...additionalProperties, + }).twice(); - return await bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); + return bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); + }); } test('paymentStatuses', () => { diff --git a/tests/unit/models/profile.test.ts b/tests/unit/models/profile.test.ts index 0a12c9e4..4d90c3ce 100644 --- a/tests/unit/models/profile.test.ts +++ b/tests/unit/models/profile.test.ts @@ -1,53 +1,52 @@ import { Profile } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getProfile(status) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/profiles/pfl_ahe8z8OPut', 200, { - resource: 'profile', - id: 'pfl_ahe8z8OPut', - mode: 'live', - name: 'My website name', - website: 'http://www.mywebsite.com', - email: 'info@mywebsite.com', - phone: '31123456789', - categoryCode: 5399, - status, - review: { - status: 'pending', - }, - createdAt: '2016-01-11T13:03:55+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - chargebacks: { - href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - methods: { - href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - payments: { - href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - refunds: { - href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', +function getProfile(status) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/profiles/pfl_ahe8z8OPut', 200, { + resource: 'profile', + id: 'pfl_ahe8z8OPut', + mode: 'live', + name: 'My website name', + website: 'http://www.mywebsite.com', + email: 'info@mywebsite.com', + phone: '31123456789', + categoryCode: 5399, + status, + review: { + status: 'pending', }, - checkoutPreviewUrl: { - href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', - type: 'text/html', + createdAt: '2016-01-11T13:03:55+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + chargebacks: { + href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + methods: { + href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + payments: { + href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + refunds: { + href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + checkoutPreviewUrl: { + href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - return await bluster(mollieClient.profiles.get.bind(mollieClient.profiles))('pfl_ahe8z8OPut'); + return bluster(mollieClient.profiles.get.bind(mollieClient.profiles))('pfl_ahe8z8OPut'); + }); } test('profileStatuses', () => { diff --git a/tests/unit/models/refund.test.ts b/tests/unit/models/refund.test.ts index 4ee2a9df..63203951 100644 --- a/tests/unit/models/refund.test.ts +++ b/tests/unit/models/refund.test.ts @@ -1,57 +1,56 @@ import { Refund } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getRefund(status) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/refunds', 200, { - _embedded: { - refunds: [ - { - resource: 'refund', - id: 're_haCsig5aru', - amount: { - value: '2.00', - currency: 'EUR', - }, - status, - createdAt: '2018-03-28T10:56:10+00:00', - description: 'My first API payment', - paymentId: 'tr_44aKxzEbr8', - settlementAmount: { - value: '-2.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', - type: 'application/hal+json', +function getRefund(status) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/refunds', 200, { + _embedded: { + refunds: [ + { + resource: 'refund', + id: 're_haCsig5aru', + amount: { + value: '2.00', + currency: 'EUR', + }, + status, + createdAt: '2018-03-28T10:56:10+00:00', + description: 'My first API payment', + paymentId: 'tr_44aKxzEbr8', + settlementAmount: { + value: '-2.00', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/refunds-api/list-refunds', - type: 'text/html', + ], }, - self: { - href: 'http://api.mollie.nl/v2/refunds?limit=10', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/refunds-api/list-refunds', + type: 'text/html', + }, + self: { + href: 'http://api.mollie.nl/v2/refunds?limit=10', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - count: 1, - }).twice(); + count: 1, + }).twice(); - return await bluster(mollieClient.refunds.page.bind(mollieClient.refunds))().then(refunds => refunds[0]); + return bluster(mollieClient.refunds.page.bind(mollieClient.refunds))().then(refunds => refunds[0]); + }); } test('refundStatuses', () => { diff --git a/tests/unit/models/subscription.test.ts b/tests/unit/models/subscription.test.ts index 0b385a45..49a62b57 100644 --- a/tests/unit/models/subscription.test.ts +++ b/tests/unit/models/subscription.test.ts @@ -1,43 +1,42 @@ import { Subscription } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -async function getSubscription(status) { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { - resource: 'subscription', - id: 'sub_wByQa6efm6', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status, - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', - type: 'application/hal+json', - }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', +function getSubscription(status) { + return new NetworkMocker(getApiKeyClientProvider()).use(([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { + resource: 'subscription', + id: 'sub_wByQa6efm6', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status, + amount: { + value: '10.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', - type: 'text/html', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - return await bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); + return bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); + }); } test('subscriptionStatuses', () => { diff --git a/tests/unit/resources/applePay.test.ts b/tests/unit/resources/applePay.test.ts index 409c1b67..85b9362b 100644 --- a/tests/unit/resources/applePay.test.ts +++ b/tests/unit/resources/applePay.test.ts @@ -1,31 +1,30 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('requestApplePayPaymentSession', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('requestApplePayPaymentSession', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/wallets/applepay/sessions', 201, { + epochTimestamp: 1555507053169, + expiresAt: 1555510653169, + merchantSessionIdentifier: 'SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24', + nonce: '0206b8db', + merchantIdentifier: 'BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB', + domainName: 'pay.example.org', + displayName: "Chuck Norris's Store", + signature: '308006092a864886f7...8cc030ad3000000000000', + }).twice(); - networkMocker.intercept('POST', '/wallets/applepay/sessions', 201, { - epochTimestamp: 1555507053169, - expiresAt: 1555510653169, - merchantSessionIdentifier: 'SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24', - nonce: '0206b8db', - merchantIdentifier: 'BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB', - domainName: 'pay.example.org', - displayName: "Chuck Norris's Store", - signature: '308006092a864886f7...8cc030ad3000000000000', - }).twice(); + const applePaySession = await bluster(mollieClient.applePay.requestPaymentSession.bind(mollieClient.applePay))({ + domain: 'pay.mywebshop.com', + validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + }); - const applePaySession = await bluster(mollieClient.applePay.requestPaymentSession.bind(mollieClient.applePay))({ - domain: 'pay.mywebshop.com', - validationUrl: 'https://apple-pay-gateway-cert.apple.com/paymentservices/paymentSession', + expect(applePaySession.epochTimestamp).toBe(1555507053169); + expect(applePaySession.expiresAt).toBe(1555510653169); + expect(applePaySession.merchantSessionIdentifier).toBe('SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24'); + expect(applePaySession.nonce).toBe('0206b8db'); + expect(applePaySession.merchantIdentifier).toBe('BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB'); + expect(applePaySession.domainName).toBe('pay.example.org'); + expect(applePaySession.displayName).toBe("Chuck Norris's Store"); + expect(applePaySession.signature).toBe('308006092a864886f7...8cc030ad3000000000000'); }); - - expect(applePaySession.epochTimestamp).toBe(1555507053169); - expect(applePaySession.expiresAt).toBe(1555510653169); - expect(applePaySession.merchantSessionIdentifier).toBe('SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24'); - expect(applePaySession.nonce).toBe('0206b8db'); - expect(applePaySession.merchantIdentifier).toBe('BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB'); - expect(applePaySession.domainName).toBe('pay.example.org'); - expect(applePaySession.displayName).toBe("Chuck Norris's Store"); - expect(applePaySession.signature).toBe('308006092a864886f7...8cc030ad3000000000000'); }); diff --git a/tests/unit/resources/chargebacks.test.ts b/tests/unit/resources/chargebacks.test.ts index 05e7e7d1..e5d28ea0 100644 --- a/tests/unit/resources/chargebacks.test.ts +++ b/tests/unit/resources/chargebacks.test.ts @@ -27,98 +27,97 @@ function testChargeback(chargeback, paymentId, chargebackId, amount) { }); } -test('listChargebacks', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/chargebacks', 200, { - _embedded: { - chargebacks: [ - { - resource: 'chargeback', - id: 'chb_n9z0tp', - amount: { - value: '-13.00', - currency: 'EUR', - }, - createdAt: '2018-03-28T11:44:32+00:00', - paymentId: 'tr_44aKxzEbr8', - settlementAmount: { - value: '-13.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/chargebacks/chb_n9z0tp', - type: 'application/hal+json', +test('listChargebacks', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/chargebacks', 200, { + _embedded: { + chargebacks: [ + { + resource: 'chargeback', + id: 'chb_n9z0tp', + amount: { + value: '-13.00', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + createdAt: '2018-03-28T11:44:32+00:00', + paymentId: 'tr_44aKxzEbr8', + settlementAmount: { + value: '-13.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/chargebacks/chb_n9z0tp', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback', + type: 'text/html', + }, }, }, - }, - { - resource: 'chargeback', - id: 'chb_6cqlwf', - amount: { - value: '-0.37', - currency: 'EUR', - }, - createdAt: '2018-03-28T11:44:32+00:00', - paymentId: 'tr_nQKWJbDj7j', - settlementAmount: { - value: '-0.37', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_nQKWJbDj7j/chargebacks/chb_6cqlwf', - type: 'application/hal+json', + { + resource: 'chargeback', + id: 'chb_6cqlwf', + amount: { + value: '-0.37', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_nQKWJbDj7j', - type: 'application/hal+json', + createdAt: '2018-03-28T11:44:32+00:00', + paymentId: 'tr_nQKWJbDj7j', + settlementAmount: { + value: '-0.37', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_nQKWJbDj7j/chargebacks/chb_6cqlwf', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_nQKWJbDj7j', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback', + type: 'text/html', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/chargebacks', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/chargebacks', + type: 'application/hal+json', + }, }, - }, - count: 2, - }).twice(); + count: 2, + }).twice(); - const chargebacks = await bluster(mollieClient.chargebacks.page.bind(mollieClient.chargebacks))(); + const chargebacks = await bluster(mollieClient.chargebacks.page.bind(mollieClient.chargebacks))(); - expect(chargebacks.length).toBe(2); + expect(chargebacks.length).toBe(2); - expect(chargebacks.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks', - type: 'text/html', - }); + expect(chargebacks.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks', + type: 'text/html', + }); - expect(chargebacks.links.self).toEqual({ - href: 'https://api.mollie.com/v2/chargebacks', - type: 'application/hal+json', - }); + expect(chargebacks.links.self).toEqual({ + href: 'https://api.mollie.com/v2/chargebacks', + type: 'application/hal+json', + }); - testChargeback(chargebacks[0], 'tr_44aKxzEbr8', 'chb_n9z0tp', '-13.00'); - testChargeback(chargebacks[1], 'tr_nQKWJbDj7j', 'chb_6cqlwf', '-0.37'); + testChargeback(chargebacks[0], 'tr_44aKxzEbr8', 'chb_n9z0tp', '-13.00'); + testChargeback(chargebacks[1], 'tr_nQKWJbDj7j', 'chb_6cqlwf', '-0.37'); + }); }); diff --git a/tests/unit/resources/customers.test.ts b/tests/unit/resources/customers.test.ts index 35bd0f15..0ad7bb59 100644 --- a/tests/unit/resources/customers.test.ts +++ b/tests/unit/resources/customers.test.ts @@ -1,169 +1,165 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('createCustomer', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/customers', 200, { - resource: 'customer', - id: 'cst_FhQJRw4s2n', - mode: 'test', - name: 'John Doe', - email: 'johndoe@example.org', - locale: null, - metadata: null, - recentlyUsedMethods: [], - createdAt: '2018-04-19T08:49:01+00:00', - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/create-customer', - type: 'text/html', +test('createCustomer', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/customers', 200, { + resource: 'customer', + id: 'cst_FhQJRw4s2n', + mode: 'test', + name: 'John Doe', + email: 'johndoe@example.org', + locale: null, + metadata: null, + recentlyUsedMethods: [], + createdAt: '2018-04-19T08:49:01+00:00', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/create-customer', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const customer = await bluster(mollieClient.customers.create.bind(mollieClient.customers))({ - name: 'John Doe', - email: 'johndoe@example.org', - }); + const customer = await bluster(mollieClient.customers.create.bind(mollieClient.customers))({ + name: 'John Doe', + email: 'johndoe@example.org', + }); - expect(customer.resource).toBe('customer'); - expect(customer.id).toBe('cst_FhQJRw4s2n'); - expect(customer.name).toBe('John Doe'); - expect(customer.email).toBe('johndoe@example.org'); - expect(customer.locale).toBeNull(); - expect(customer.metadata).toBeNull(); - expect(customer.recentlyUsedMethods).toEqual([]); - expect(customer.createdAt).toBe('2018-04-19T08:49:01+00:00'); - - expect(customer._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/customers-api/create-customer', - type: 'text/html', + expect(customer.resource).toBe('customer'); + expect(customer.id).toBe('cst_FhQJRw4s2n'); + expect(customer.name).toBe('John Doe'); + expect(customer.email).toBe('johndoe@example.org'); + expect(customer.locale).toBeNull(); + expect(customer.metadata).toBeNull(); + expect(customer.recentlyUsedMethods).toEqual([]); + expect(customer.createdAt).toBe('2018-04-19T08:49:01+00:00'); + + expect(customer._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/customers-api/create-customer', + type: 'text/html', + }); }); }); -test('getCustomer', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n', 200, { - resource: 'customer', - id: 'cst_FhQJRw4s2n', - mode: 'test', - name: 'John Doe', - email: 'johndoe@example.org', - locale: null, - metadata: null, - recentlyUsedMethods: [], - createdAt: '2018-04-19T08:49:01+00:00', - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', - type: 'text/html', +test('getCustomer', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n', 200, { + resource: 'customer', + id: 'cst_FhQJRw4s2n', + mode: 'test', + name: 'John Doe', + email: 'johndoe@example.org', + locale: null, + metadata: null, + recentlyUsedMethods: [], + createdAt: '2018-04-19T08:49:01+00:00', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', + type: 'text/html', + }, }, - }, - }).twice(); - - const customer = await bluster(mollieClient.customers.get.bind(mollieClient.customers))('cst_FhQJRw4s2n'); - - expect(customer.resource).toBe('customer'); - expect(customer.id).toBe('cst_FhQJRw4s2n'); - expect(customer.name).toBe('John Doe'); - expect(customer.email).toBe('johndoe@example.org'); - expect(customer.locale).toBeNull(); - expect(customer.metadata).toBeNull(); - expect(customer.recentlyUsedMethods).toEqual([]); - expect(customer.createdAt).toBe('2018-04-19T08:49:01+00:00'); - - expect(customer._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', - type: 'text/html', + }).twice(); + + const customer = await bluster(mollieClient.customers.get.bind(mollieClient.customers))('cst_FhQJRw4s2n'); + + expect(customer.resource).toBe('customer'); + expect(customer.id).toBe('cst_FhQJRw4s2n'); + expect(customer.name).toBe('John Doe'); + expect(customer.email).toBe('johndoe@example.org'); + expect(customer.locale).toBeNull(); + expect(customer.metadata).toBeNull(); + expect(customer.recentlyUsedMethods).toEqual([]); + expect(customer.createdAt).toBe('2018-04-19T08:49:01+00:00'); + + expect(customer._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', + type: 'text/html', + }); }); }); -test('listCustomers', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers', 200, { - _embedded: { - customers: [ - { - resource: 'customer', - id: 'cst_FhQJRw4s2n', - mode: 'test', - name: 'John Doe', - email: 'johndoe@example.org', - locale: null, - metadata: null, - recentlyUsedMethods: [], - createdAt: '2018-04-19T08:49:01+00:00', - }, - ], - }, - count: 1, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', - type: 'text/html', +test('listCustomers', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers', 200, { + _embedded: { + customers: [ + { + resource: 'customer', + id: 'cst_FhQJRw4s2n', + mode: 'test', + name: 'John Doe', + email: 'johndoe@example.org', + locale: null, + metadata: null, + recentlyUsedMethods: [], + createdAt: '2018-04-19T08:49:01+00:00', + }, + ], }, - self: { - href: 'https://api.mollie.com/v2/customers?limit=50', - type: 'application/hal+json', + count: 1, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const customers = await bluster(mollieClient.customers.page.bind(mollieClient.customers))(); + const customers = await bluster(mollieClient.customers.page.bind(mollieClient.customers))(); - expect(customers.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', - type: 'text/html', - }); + expect(customers.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', + type: 'text/html', + }); - expect(customers.links.self).toEqual({ - href: 'https://api.mollie.com/v2/customers?limit=50', - type: 'application/hal+json', - }); + expect(customers.links.self).toEqual({ + href: 'https://api.mollie.com/v2/customers?limit=50', + type: 'application/hal+json', + }); - customers.forEach(customer => { - expect(customer.resource).toBe('customer'); - expect(customer.createdAt).toBeTruthy(); + customers.forEach(customer => { + expect(customer.resource).toBe('customer'); + expect(customer.createdAt).toBeTruthy(); + }); }); }); -test('updateCustomer', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - const expectedName = 'Kaas Broodje'; - const expectedEmail = 'kaas.broodje@gmail.com'; - - networkMocker.intercept('PATCH', '/customers/cst_FhQJRw4s2n', 200, { - resource: 'customer', - id: 'cst_FhQJRw4s2n', - mode: 'test', - name: expectedName, - email: expectedEmail, - locale: null, - metadata: null, - recentlyUsedMethods: [], - createdAt: '2018-04-19T08:49:01+00:00', - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', - type: 'text/html', +test('updateCustomer', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + const expectedName = 'Kaas Broodje'; + const expectedEmail = 'kaas.broodje@gmail.com'; + + networkMocker.intercept('PATCH', '/customers/cst_FhQJRw4s2n', 200, { + resource: 'customer', + id: 'cst_FhQJRw4s2n', + mode: 'test', + name: expectedName, + email: expectedEmail, + locale: null, + metadata: null, + recentlyUsedMethods: [], + createdAt: '2018-04-19T08:49:01+00:00', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/get-customer', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const updatedCustomer = await bluster(mollieClient.customers.update.bind(mollieClient.customers))('cst_FhQJRw4s2n', { - name: expectedName, - email: expectedEmail, - }); + const updatedCustomer = await bluster(mollieClient.customers.update.bind(mollieClient.customers))('cst_FhQJRw4s2n', { + name: expectedName, + email: expectedEmail, + }); - expect(updatedCustomer.name).toBe(expectedName); - expect(updatedCustomer.email).toBe(expectedEmail); + expect(updatedCustomer.name).toBe(expectedName); + expect(updatedCustomer.email).toBe(expectedEmail); + }); }); diff --git a/tests/unit/resources/customers/mandates.test.ts b/tests/unit/resources/customers/mandates.test.ts index 9c300328..c4cca913 100644 --- a/tests/unit/resources/customers/mandates.test.ts +++ b/tests/unit/resources/customers/mandates.test.ts @@ -1,168 +1,165 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; -test('createCustomerMandate', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/mandates', 200, { - resource: 'mandate', - id: 'mdt_AcQl5fdL4h', - status: 'valid', - method: 'directdebit', - details: { - consumerName: 'John Doe', - consumerAccount: 'NL55INGB0000000000', - consumerBic: 'INGBNL2A', - }, - mandateReference: null, - signatureDate: '2018-05-07', - createdAt: '2018-05-07T10:49:08+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', - type: 'application/hal+json', +test('createCustomerMandate', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/mandates', 200, { + resource: 'mandate', + id: 'mdt_AcQl5fdL4h', + status: 'valid', + method: 'directdebit', + details: { + consumerName: 'John Doe', + consumerAccount: 'NL55INGB0000000000', + consumerBic: 'INGBNL2A', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', - }, - documentation: { - href: 'https://mollie.com/en/docs/reference/customers/create-mandate', - type: 'text/html', + mandateReference: null, + signatureDate: '2018-05-07', + createdAt: '2018-05-07T10:49:08+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://mollie.com/en/docs/reference/customers/create-mandate', + type: 'text/html', + }, }, - }, - }).twice(); - - const mandate = await bluster(mollieClient.customerMandates.create.bind(mollieClient.customerMandates))({ - customerId: 'cst_FhQJRw4s2n', - consumerName: 'John Doe', - method: 'directdebit', - consumerBic: 'INGBNL2A', - consumerAccount: 'NL55INGB0000000000', - }); + }).twice(); - expect(mandate.resource).toBe('mandate'); - expect(mandate.status).toBe('valid'); - expect(mandate.details).toEqual({ consumerName: 'John Doe', consumerAccount: 'NL55INGB0000000000', consumerBic: 'INGBNL2A' }); - expect(mandate.mandateReference).toBeNull(); - expect(mandate.signatureDate).toBe('2018-05-07'); - expect(mandate.createdAt).toBe('2018-05-07T10:49:08+00:00'); + const mandate = await bluster(mollieClient.customerMandates.create.bind(mollieClient.customerMandates))({ + customerId: 'cst_FhQJRw4s2n', + consumerName: 'John Doe', + method: 'directdebit', + consumerBic: 'INGBNL2A', + consumerAccount: 'NL55INGB0000000000', + }); - expect(mandate._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', type: 'application/hal+json' }); + expect(mandate.resource).toBe('mandate'); + expect(mandate.status).toBe('valid'); + expect(mandate.details).toEqual({ consumerName: 'John Doe', consumerAccount: 'NL55INGB0000000000', consumerBic: 'INGBNL2A' }); + expect(mandate.mandateReference).toBeNull(); + expect(mandate.signatureDate).toBe('2018-05-07'); + expect(mandate.createdAt).toBe('2018-05-07T10:49:08+00:00'); - expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + expect(mandate._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', type: 'application/hal+json' }); - expect(mandate._links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/create-mandate', type: 'text/html' }); -}); + expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); -test('getCustomerMandate', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); + expect(mandate._links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/create-mandate', type: 'text/html' }); + }); +}); - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', 200, { - resource: 'mandate', - id: 'mdt_AcQl5fdL4h', - status: 'valid', - method: 'directdebit', - details: { - consumerName: 'John Doe', - consumerAccount: 'NL55INGB0000000000', - consumerBic: 'INGBNL2A', - }, - mandateReference: null, - signatureDate: '2018-05-07', - createdAt: '2018-05-07T10:49:08+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', - type: 'application/hal+json', - }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', +test('getCustomerMandate', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', 200, { + resource: 'mandate', + id: 'mdt_AcQl5fdL4h', + status: 'valid', + method: 'directdebit', + details: { + consumerName: 'John Doe', + consumerAccount: 'NL55INGB0000000000', + consumerBic: 'INGBNL2A', }, - documentation: { - href: 'https://mollie.com/en/docs/reference/customers/create-mandate', - type: 'text/html', + mandateReference: null, + signatureDate: '2018-05-07', + createdAt: '2018-05-07T10:49:08+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://mollie.com/en/docs/reference/customers/create-mandate', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const mandate = await bluster(mollieClient.customerMandates.get.bind(mollieClient.customerMandates))('mdt_AcQl5fdL4h', { customerId: 'cst_FhQJRw4s2n' }); + const mandate = await bluster(mollieClient.customerMandates.get.bind(mollieClient.customerMandates))('mdt_AcQl5fdL4h', { customerId: 'cst_FhQJRw4s2n' }); - expect(mandate.resource).toBe('mandate'); - expect(mandate.status).toBe('valid'); - expect(mandate.method).toBe('directdebit'); - expect(mandate.details).toEqual({ consumerName: 'John Doe', consumerAccount: 'NL55INGB0000000000', consumerBic: 'INGBNL2A' }); - expect(mandate.mandateReference).toBeNull(); - expect(mandate.signatureDate).toBe('2018-05-07'); - expect(mandate.createdAt).toBe('2018-05-07T10:49:08+00:00'); + expect(mandate.resource).toBe('mandate'); + expect(mandate.status).toBe('valid'); + expect(mandate.method).toBe('directdebit'); + expect(mandate.details).toEqual({ consumerName: 'John Doe', consumerAccount: 'NL55INGB0000000000', consumerBic: 'INGBNL2A' }); + expect(mandate.mandateReference).toBeNull(); + expect(mandate.signatureDate).toBe('2018-05-07'); + expect(mandate.createdAt).toBe('2018-05-07T10:49:08+00:00'); - expect(mandate._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', type: 'application/hal+json' }); + expect(mandate._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', type: 'application/hal+json' }); - expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); - expect(mandate._links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/create-mandate', type: 'text/html' }); + expect(mandate._links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/create-mandate', type: 'text/html' }); + }); }); -test('getCustomerMandates', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates', 200, { - _embedded: { - mandates: [ - { - resource: 'mandate', - id: 'mdt_AcQl5fdL4h', - status: 'valid', - method: 'directdebit', - details: { - consumerName: 'John Doe', - consumerAccount: 'NL55INGB0000000000', - consumerBic: 'INGBNL2A', - }, - mandateReference: null, - signatureDate: '2018-05-07', - createdAt: '2018-05-07T10:49:08+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', - type: 'application/hal+json', +test('getCustomerMandates', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/mandates', 200, { + _embedded: { + mandates: [ + { + resource: 'mandate', + id: 'mdt_AcQl5fdL4h', + status: 'valid', + method: 'directdebit', + details: { + consumerName: 'John Doe', + consumerAccount: 'NL55INGB0000000000', + consumerBic: 'INGBNL2A', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + mandateReference: null, + signatureDate: '2018-05-07', + createdAt: '2018-05-07T10:49:08+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/mandates/mdt_AcQl5fdL4h', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - count: 1, - _links: { - documentation: { - href: 'https://mollie.com/en/docs/reference/customers/list-mandates', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/customers/cst_vzEExMcxj7/mandates?limit=50', - type: 'application/hal+json', + count: 1, + _links: { + documentation: { + href: 'https://mollie.com/en/docs/reference/customers/list-mandates', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers/cst_vzEExMcxj7/mandates?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const mandates = await bluster(mollieClient.customerMandates.page.bind(mollieClient.customerMandates))({ customerId: 'cst_FhQJRw4s2n' }); + const mandates = await bluster(mollieClient.customerMandates.page.bind(mollieClient.customerMandates))({ customerId: 'cst_FhQJRw4s2n' }); - mandates.forEach(mandate => { - expect(mandate.resource).toBe('mandate'); - expect(mandate.status).toBe('valid'); + mandates.forEach(mandate => { + expect(mandate.resource).toBe('mandate'); + expect(mandate.status).toBe('valid'); - expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); - }); + expect(mandate._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + }); - expect(mandates.links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_vzEExMcxj7/mandates?limit=50', type: 'application/hal+json' }); + expect(mandates.links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_vzEExMcxj7/mandates?limit=50', type: 'application/hal+json' }); - expect(mandates.links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/list-mandates', type: 'text/html' }); + expect(mandates.links.documentation).toEqual({ href: 'https://mollie.com/en/docs/reference/customers/list-mandates', type: 'text/html' }); + }); }); diff --git a/tests/unit/resources/customers/payments.test.ts b/tests/unit/resources/customers/payments.test.ts index 530c1f04..f1076aaa 100644 --- a/tests/unit/resources/customers/payments.test.ts +++ b/tests/unit/resources/customers/payments.test.ts @@ -1,235 +1,233 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; -test('createCustomerPayment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/payments', 201, { - resource: 'payment', - id: 'tr_44aKxzEbr8', - mode: 'test', - createdAt: '2018-03-13T14:02:29+00:00', - amount: { - value: '20.00', - currency: 'EUR', - }, - description: 'My first API payment', - method: null, - metadata: { - order_id: '1234', - }, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-13T14:17:29+00:00', - details: null, - profileId: 'pfl_2A1gacu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', - }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', - type: 'text/html', +test('createCustomerPayment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/payments', 201, { + resource: 'payment', + id: 'tr_44aKxzEbr8', + mode: 'test', + createdAt: '2018-03-13T14:02:29+00:00', + amount: { + value: '20.00', + currency: 'EUR', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + description: 'My first API payment', + method: null, + metadata: { + order_id: '1234', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/create-payment', - type: 'text/html', + status: 'open', + isCancelable: false, + expiresAt: '2018-03-13T14:17:29+00:00', + details: null, + profileId: 'pfl_2A1gacu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', + type: 'text/html', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/create-payment', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const payment = await bluster(mollieClient.customerPayments.create.bind(mollieClient.customerPayments))({ - amount: { - currency: 'EUR', - value: '20.00', - }, - customerId: 'cst_FhQJRw4s2n', - description: 'My first API payment', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - metadata: { - order_id: '1234', - }, - }); + const payment = await bluster(mollieClient.customerPayments.create.bind(mollieClient.customerPayments))({ + amount: { + currency: 'EUR', + value: '20.00', + }, + customerId: 'cst_FhQJRw4s2n', + description: 'My first API payment', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + metadata: { + order_id: '1234', + }, + }); - expect(payment.id).toBe('tr_44aKxzEbr8'); - expect(payment.mode).toBe('test'); - expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); + expect(payment.id).toBe('tr_44aKxzEbr8'); + expect(payment.mode).toBe('test'); + expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); - expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); + expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); - expect(payment.description).toBe('My first API payment'); - expect(payment.method).toBeNull(); - expect(payment.metadata).toEqual({ order_id: '1234' }); - expect(payment.status).toBe('open'); - expect(payment.isCancelable).toBe(false); - expect(payment.expiresAt).toBe('2018-03-13T14:17:29+00:00'); - expect(payment.details).toBeNull(); - expect(payment.profileId).toBe('pfl_2A1gacu42V'); - expect(payment.sequenceType).toBe('oneoff'); - expect(payment.redirectUrl).toBe('https://example.org/redirect'); - expect(payment.cancelUrl).toBe('https://example.org/cancel'); - expect(payment.webhookUrl).toBe('https://example.org/webhook'); + expect(payment.description).toBe('My first API payment'); + expect(payment.method).toBeNull(); + expect(payment.metadata).toEqual({ order_id: '1234' }); + expect(payment.status).toBe('open'); + expect(payment.isCancelable).toBe(false); + expect(payment.expiresAt).toBe('2018-03-13T14:17:29+00:00'); + expect(payment.details).toBeNull(); + expect(payment.profileId).toBe('pfl_2A1gacu42V'); + expect(payment.sequenceType).toBe('oneoff'); + expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); + expect(payment.webhookUrl).toBe('https://example.org/webhook'); - expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); + expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); - expect(payment._links.checkout).toEqual({ href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', type: 'text/html' }); + expect(payment._links.checkout).toEqual({ href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', type: 'text/html' }); - expect(payment._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + expect(payment._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); - expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/customers-api/create-payment', type: 'text/html' }); + expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/customers-api/create-payment', type: 'text/html' }); + }); }); -test('listCustomerPayouts', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/payments', 200, { - _embedded: { - payments: [ - { - resource: 'payment', - id: 'tr_admNa2tFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', - }, - description: 'Payment no 1', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_admNa2tFfa', - type: 'application/hal+json', +test('listCustomerPayouts', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/payments', 200, { + _embedded: { + payments: [ + { + resource: 'payment', + id: 'tr_admNa2tFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/admNa2tFfa', - type: 'text/html', + description: 'Payment no 1', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_admNa2tFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/admNa2tFfa', + type: 'text/html', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', - }, - }, - }, - { - resource: 'payment', - id: 'tr_bcaLc7hFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', }, - description: 'Payment no 2', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_bcaLc7hFfa', - type: 'application/hal+json', + { + resource: 'payment', + id: 'tr_bcaLc7hFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/bcaLc7hFfa', - type: 'text/html', + description: 'Payment no 2', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_bcaLc7hFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/bcaLc7hFfa', + type: 'text/html', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', - }, - }, - }, - { - resource: 'payment', - id: 'tr_pslHy1tFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', }, - description: 'Payment no 3', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_pslHy1tFfa', - type: 'application/hal+json', + { + resource: 'payment', + id: 'tr_pslHy1tFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/pslHy1tFfa', - type: 'text/html', - }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + description: 'Payment no 3', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_pslHy1tFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/pslHy1tFfa', + type: 'text/html', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customer-payments', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/customers/cst_TkNdP8yPrH/payments?limit=50', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customer-payments', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers/cst_TkNdP8yPrH/payments?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - count: 3, - }).twice(); + count: 3, + }).twice(); - const payments = await bluster(mollieClient.customerPayments.page.bind(mollieClient.customerPayments))({ customerId: 'cst_FhQJRw4s2n' }); + const payments = await bluster(mollieClient.customerPayments.page.bind(mollieClient.customerPayments))({ customerId: 'cst_FhQJRw4s2n' }); - expect(payments.length).toBe(3); + expect(payments.length).toBe(3); - expect(payments.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customer-payments', - type: 'text/html', - }); + expect(payments.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customer-payments', + type: 'text/html', + }); - expect(payments.links.self).toEqual({ - href: 'https://api.mollie.com/v2/customers/cst_TkNdP8yPrH/payments?limit=50', - type: 'application/hal+json', + expect(payments.links.self).toEqual({ + href: 'https://api.mollie.com/v2/customers/cst_TkNdP8yPrH/payments?limit=50', + type: 'application/hal+json', + }); }); }); diff --git a/tests/unit/resources/customers/subscriptions.test.ts b/tests/unit/resources/customers/subscriptions.test.ts index 16801808..26a46732 100644 --- a/tests/unit/resources/customers/subscriptions.test.ts +++ b/tests/unit/resources/customers/subscriptions.test.ts @@ -1,298 +1,293 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; -test('createCustomerSubscription', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { - resource: 'subscription', - id: 'sub_wByQa6efm6', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status: 'active', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', - type: 'application/hal+json', +test('createCustomerSubscription', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { + resource: 'subscription', + id: 'sub_wByQa6efm6', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status: 'active', + amount: { + value: '10.00', + currency: 'EUR', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', + type: 'text/html', + }, }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', - type: 'text/html', + }).twice(); + + const subscription = await bluster(mollieClient.customerSubscriptions.create.bind(mollieClient.customerSubscriptions))({ + customerId: 'cst_FhQJRw4s2n', + amount: { + value: '10.00', + currency: 'EUR', }, - }, - }).twice(); + interval: '1 month', + description: 'Order 1234', + }); - const subscription = await bluster(mollieClient.customerSubscriptions.create.bind(mollieClient.customerSubscriptions))({ - customerId: 'cst_FhQJRw4s2n', - amount: { - value: '10.00', - currency: 'EUR', - }, - interval: '1 month', - description: 'Order 1234', + expect(subscription.resource).toBe('subscription'); + expect(subscription.id).toBe('sub_wByQa6efm6'); + expect(subscription.mode).toBe('test'); + expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); + expect(subscription.status).toBe('active'); + expect(subscription.amount).toEqual({ value: '10.00', currency: 'EUR' }); + expect(subscription.description).toBe('Order 1234'); + expect(subscription.method).toBeNull(); + expect(subscription.times).toBeNull(); + expect(subscription.interval).toBe('1 month'); + expect(subscription.startDate).toBe('2018-04-24'); + + expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', type: 'application/hal+json' }); + + expect(subscription._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + + expect(subscription._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', type: 'text/html' }); }); - - expect(subscription.resource).toBe('subscription'); - expect(subscription.id).toBe('sub_wByQa6efm6'); - expect(subscription.mode).toBe('test'); - expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); - expect(subscription.status).toBe('active'); - expect(subscription.amount).toEqual({ value: '10.00', currency: 'EUR' }); - expect(subscription.description).toBe('Order 1234'); - expect(subscription.method).toBeNull(); - expect(subscription.times).toBeNull(); - expect(subscription.interval).toBe('1 month'); - expect(subscription.startDate).toBe('2018-04-24'); - - expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', type: 'application/hal+json' }); - - expect(subscription._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); - - expect(subscription._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription', type: 'text/html' }); }); -test('getCustomerSubscription', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { - resource: 'subscription', - id: 'sub_wByQa6efm6', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status: 'active', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', - type: 'application/hal+json', +test('getCustomerSubscription', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', 200, { + resource: 'subscription', + id: 'sub_wByQa6efm6', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status: 'active', + amount: { + value: '10.00', + currency: 'EUR', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription', - type: 'text/html', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription', + type: 'text/html', + }, }, - }, - }).twice(); - - const subscription = await bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); - - expect(subscription.resource).toBe('subscription'); - expect(subscription.id).toBe('sub_wByQa6efm6'); - expect(subscription.mode).toBe('test'); - expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); - expect(subscription.status).toBe('active'); - expect(subscription.amount).toEqual({ value: '10.00', currency: 'EUR' }); - expect(subscription.description).toBe('Order 1234'); - expect(subscription.method).toBeNull(); - expect(subscription.times).toBeNull(); - expect(subscription.interval).toBe('1 month'); - expect(subscription.startDate).toBe('2018-04-24'); + }).twice(); - expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', type: 'application/hal+json' }); + const subscription = await bluster(mollieClient.customerSubscriptions.get.bind(mollieClient.customerSubscriptions))('sub_wByQa6efm6', { customerId: 'cst_FhQJRw4s2n' }); - expect(subscription._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); - - expect(subscription._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription', type: 'text/html' }); + expect(subscription.resource).toBe('subscription'); + expect(subscription.id).toBe('sub_wByQa6efm6'); + expect(subscription.mode).toBe('test'); + expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); + expect(subscription.status).toBe('active'); + expect(subscription.amount).toEqual({ value: '10.00', currency: 'EUR' }); + expect(subscription.description).toBe('Order 1234'); + expect(subscription.method).toBeNull(); + expect(subscription.times).toBeNull(); + expect(subscription.interval).toBe('1 month'); + expect(subscription.startDate).toBe('2018-04-24'); + + expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', type: 'application/hal+json' }); + + expect(subscription._links.customer).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', type: 'application/hal+json' }); + + expect(subscription._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription', type: 'text/html' }); + }); }); -test('getCustomerSubscriptions', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { - _embedded: { - subscriptions: [ - { - resource: 'subscription', - id: 'sub_wByQa6efm6', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status: 'active', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', - type: 'application/hal+json', +test('getCustomerSubscriptions', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_FhQJRw4s2n/subscriptions', 200, { + _embedded: { + subscriptions: [ + { + resource: 'subscription', + id: 'sub_wByQa6efm6', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status: 'active', + amount: { + value: '10.00', + currency: 'EUR', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - count: 1, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions?limit=50', - type: 'application/hal+json', + count: 1, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const subscriptions = await bluster(mollieClient.customerSubscriptions.page.bind(mollieClient.customerSubscriptions))({ customerId: 'cst_FhQJRw4s2n' }); + const subscriptions = await bluster(mollieClient.customerSubscriptions.page.bind(mollieClient.customerSubscriptions))({ customerId: 'cst_FhQJRw4s2n' }); - expect(subscriptions.length).toBe(1); + expect(subscriptions.length).toBe(1); - expect(subscriptions.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', type: 'text/html' }); + expect(subscriptions.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', type: 'text/html' }); - expect(subscriptions.links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions?limit=50', type: 'application/hal+json' }); + expect(subscriptions.links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions?limit=50', type: 'application/hal+json' }); - subscriptions.forEach(subscription => { - expect(subscription.resource).toBe('subscription'); - expect(subscription.createdAt).toBeTruthy(); + subscriptions.forEach(subscription => { + expect(subscription.resource).toBe('subscription'); + expect(subscription.createdAt).toBeTruthy(); + }); }); }); -test('cancelCustomerSubscription', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('DELETE', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { - resource: 'subscription', - id: 'sub_DRjwaT5qHx', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status: 'canceled', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - canceledAt: '2018-04-24T12:31:32+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', - type: 'application/hal+json', - }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j', - type: 'application/hal+json', +test('cancelCustomerSubscription', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('DELETE', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { + resource: 'subscription', + id: 'sub_DRjwaT5qHx', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status: 'canceled', + amount: { + value: '10.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription', - type: 'text/html', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + canceledAt: '2018-04-24T12:31:32+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription', + type: 'text/html', + }, }, - }, - }).twice(); - - const subscription = await bluster(mollieClient.customerSubscriptions.cancel.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { customerId: 'cst_VhjQebNW5j' }); - - expect(subscription.resource).toBe('subscription'); - expect(subscription.id).toBe('sub_DRjwaT5qHx'); - expect(subscription.mode).toBe('test'); - expect(subscription.status).toBe('canceled'); - expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); - expect(subscription.canceledAt).toBe('2018-04-24T12:31:32+00:00'); + }).twice(); - expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', type: 'application/hal+json' }); + const subscription = await bluster(mollieClient.customerSubscriptions.cancel.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { customerId: 'cst_VhjQebNW5j' }); - expect(subscription._links.customer).toEqual({ - href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j', - type: 'application/hal+json', - }); - - expect(subscription._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription', - type: 'text/html', + expect(subscription.resource).toBe('subscription'); + expect(subscription.id).toBe('sub_DRjwaT5qHx'); + expect(subscription.mode).toBe('test'); + expect(subscription.status).toBe('canceled'); + expect(subscription.createdAt).toBe('2018-04-24T11:41:55+00:00'); + expect(subscription.canceledAt).toBe('2018-04-24T12:31:32+00:00'); + + expect(subscription._links.self).toEqual({ href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', type: 'application/hal+json' }); + + expect(subscription._links.customer).toEqual({ + href: 'https://api.mollie.com/v2/customers/cst_VhjQebNW5j', + type: 'application/hal+json', + }); + + expect(subscription._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription', + type: 'text/html', + }); }); }); -test('updateCustomerSubscription', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - const expectedAmountValue = '12.00'; - const expectedAmountCurrency = 'EUR'; - const expectedStartDate = '2018-12-12'; - - networkMocker.intercept('PATCH', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { - resource: 'subscription', - id: 'sub_DRjwaT5qHx', - customerId: 'cst_VhjQebNW5j', - mode: 'live', - createdAt: '2018-07-17T07:45:52+00:00', - status: 'active', - amount: { - value: expectedAmountValue, - currency: expectedAmountCurrency, - }, - description: 'Mollie Recurring subscription #1', - method: null, - times: 42, - interval: '15 days', - startDate: expectedStartDate, - webhookUrl: 'https://example.org/webhook', - _links: { - self: { - href: 'http://api.mollie.test/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', - type: 'application/hal+json', - }, - customer: { - href: 'http://api.mollie.test/v2/customers/cst_VhjQebNW5j', - type: 'application/hal+json', +test('updateCustomerSubscription', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + const expectedAmountValue = '12.00'; + const expectedAmountCurrency = 'EUR'; + const expectedStartDate = '2018-12-12'; + + networkMocker.intercept('PATCH', '/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', 200, { + resource: 'subscription', + id: 'sub_DRjwaT5qHx', + customerId: 'cst_VhjQebNW5j', + mode: 'live', + createdAt: '2018-07-17T07:45:52+00:00', + status: 'active', + amount: { + value: expectedAmountValue, + currency: expectedAmountCurrency, }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/update-subscription', - type: 'text/html', + description: 'Mollie Recurring subscription #1', + method: null, + times: 42, + interval: '15 days', + startDate: expectedStartDate, + webhookUrl: 'https://example.org/webhook', + _links: { + self: { + href: 'http://api.mollie.test/v2/customers/cst_VhjQebNW5j/subscriptions/sub_DRjwaT5qHx', + type: 'application/hal+json', + }, + customer: { + href: 'http://api.mollie.test/v2/customers/cst_VhjQebNW5j', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/update-subscription', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const subscription = await bluster(mollieClient.customerSubscriptions.update.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { - customerId: 'cst_VhjQebNW5j', - amount: { value: expectedAmountValue, currency: expectedAmountCurrency }, - startDate: expectedStartDate, - }); + const subscription = await bluster(mollieClient.customerSubscriptions.update.bind(mollieClient.customerSubscriptions))('sub_DRjwaT5qHx', { + customerId: 'cst_VhjQebNW5j', + amount: { value: expectedAmountValue, currency: expectedAmountCurrency }, + startDate: expectedStartDate, + }); - expect(subscription.startDate).toBe(expectedStartDate); - expect(subscription.amount).toEqual({ value: expectedAmountValue, currency: expectedAmountCurrency }); + expect(subscription.startDate).toBe(expectedStartDate); + expect(subscription.amount).toEqual({ value: expectedAmountValue, currency: expectedAmountCurrency }); + }); }); diff --git a/tests/unit/resources/onboarding.test.ts b/tests/unit/resources/onboarding.test.ts index bb530dc9..909f70d3 100644 --- a/tests/unit/resources/onboarding.test.ts +++ b/tests/unit/resources/onboarding.test.ts @@ -1,61 +1,59 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('get', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/onboarding/me', 200, { - resource: 'onboarding', - name: 'Mollie B.V.', - signedUpAt: '2018-12-20T10:49:08+00:00', - status: 'completed', - canReceivePayments: true, - canReceiveSettlements: true, - _links: { - self: { - href: 'https://api.mollie.com/v2/onboarding/me', - type: 'application/hal+json', +test('get', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/onboarding/me', 200, { + resource: 'onboarding', + name: 'Mollie B.V.', + signedUpAt: '2018-12-20T10:49:08+00:00', + status: 'completed', + canReceivePayments: true, + canReceiveSettlements: true, + _links: { + self: { + href: 'https://api.mollie.com/v2/onboarding/me', + type: 'application/hal+json', + }, + dashboard: { + href: 'https://www.mollie.com/dashboard/onboarding', + type: 'text/html', + }, + organization: { + href: 'https://api.mollie.com/v2/organization/org_12345', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', + type: 'text/html', + }, }, - dashboard: { - href: 'https://www.mollie.com/dashboard/onboarding', - type: 'text/html', - }, - organization: { - href: 'https://api.mollie.com/v2/organization/org_12345', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', - type: 'text/html', - }, - }, - }).twice(); + }).twice(); - const onboarding = await bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); + const onboarding = await bluster(mollieClient.onboarding.get.bind(mollieClient.onboarding))(); - expect(onboarding.resource).toBe('onboarding'); - expect(onboarding.name).toBe('Mollie B.V.'); - expect(onboarding.status).toBe('completed'); - expect(onboarding.signedUpAt).toBe('2018-12-20T10:49:08+00:00'); - expect(onboarding.canReceivePayments).toBe(true); - expect(onboarding.canReceiveSettlements).toBe(true); + expect(onboarding.resource).toBe('onboarding'); + expect(onboarding.name).toBe('Mollie B.V.'); + expect(onboarding.status).toBe('completed'); + expect(onboarding.signedUpAt).toBe('2018-12-20T10:49:08+00:00'); + expect(onboarding.canReceivePayments).toBe(true); + expect(onboarding.canReceiveSettlements).toBe(true); - expect(onboarding._links.self).toEqual({ href: 'https://api.mollie.com/v2/onboarding/me', type: 'application/hal+json' }); + expect(onboarding._links.self).toEqual({ href: 'https://api.mollie.com/v2/onboarding/me', type: 'application/hal+json' }); - expect(onboarding._links.dashboard).toEqual({ href: 'https://www.mollie.com/dashboard/onboarding', type: 'text/html' }); + expect(onboarding._links.dashboard).toEqual({ href: 'https://www.mollie.com/dashboard/onboarding', type: 'text/html' }); - expect(onboarding._links.organization).toEqual({ href: 'https://api.mollie.com/v2/organization/org_12345', type: 'application/hal+json' }); + expect(onboarding._links.organization).toEqual({ href: 'https://api.mollie.com/v2/organization/org_12345', type: 'application/hal+json' }); - expect(onboarding._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', type: 'text/html' }); + expect(onboarding._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/onboarding-api/get-onboarding-status', type: 'text/html' }); + }); }); -test('submit', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/onboarding/me', 204).twice(); +test('submit', () => { + new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/onboarding/me', 204).twice(); - const result = await bluster(mollieClient.onboarding.submit.bind(mollieClient.onboarding))(); + const result = await bluster(mollieClient.onboarding.submit.bind(mollieClient.onboarding))(); - expect(result).toBe(true); + expect(result).toBe(true); + }); }); diff --git a/tests/unit/resources/orders.test.ts b/tests/unit/resources/orders.test.ts index a96e0b44..aee6ddf2 100644 --- a/tests/unit/resources/orders.test.ts +++ b/tests/unit/resources/orders.test.ts @@ -239,440 +239,434 @@ function testOrder(order, orderId, orderStatus = 'created', orderNumber = '1337' }); } -test('createOrder', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/orders', 201, composeOrderResponse('ord_pbjz8x')).twice(); - - const order = await bluster(mollieClient.orders.create.bind(mollieClient.orders))({ - amount: { - value: '1027.99', - currency: 'EUR', - }, - billingAddress: { - organizationName: 'Organization Name LTD.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - shippingAddress: { - organizationName: 'Organization Name LTD.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - metadata: { - order_id: '1337', - description: 'Lego cars', - }, - consumerDateOfBirth: '1958-01-31', - locale: 'nl_NL', - orderNumber: '1337', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - method: 'klarnapaylater', - lines: [ - { - sku: '5702016116977', - name: 'LEGO 42083 Bugatti Chiron', - productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', - imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', - quantity: 2, - vatRate: '21.00', - unitPrice: { - currency: 'EUR', - value: '399.00', - }, - totalAmount: { - currency: 'EUR', - value: '698.00', - }, - discountAmount: { - currency: 'EUR', - value: '100.00', - }, - vatAmount: { - currency: 'EUR', - value: '121.14', - }, +test('createOrder', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/orders', 201, composeOrderResponse('ord_pbjz8x')).twice(); + + const order = await bluster(mollieClient.orders.create.bind(mollieClient.orders))({ + amount: { + value: '1027.99', + currency: 'EUR', }, - { - type: 'digital', - sku: '5702015594028', - name: 'LEGO 42056 Porsche 911 GT3 RS', - productUrl: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', - imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', - quantity: 1, - vatRate: '21.00', - unitPrice: { - currency: 'EUR', - value: '329.99', - }, - totalAmount: { - currency: 'EUR', - value: '329.99', + billingAddress: { + organizationName: 'Organization Name LTD.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + shippingAddress: { + organizationName: 'Organization Name LTD.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + metadata: { + order_id: '1337', + description: 'Lego cars', + }, + consumerDateOfBirth: '1958-01-31', + locale: 'nl_NL', + orderNumber: '1337', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + method: 'klarnapaylater', + lines: [ + { + sku: '5702016116977', + name: 'LEGO 42083 Bugatti Chiron', + productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', + imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', + quantity: 2, + vatRate: '21.00', + unitPrice: { + currency: 'EUR', + value: '399.00', + }, + totalAmount: { + currency: 'EUR', + value: '698.00', + }, + discountAmount: { + currency: 'EUR', + value: '100.00', + }, + vatAmount: { + currency: 'EUR', + value: '121.14', + }, }, - vatAmount: { - currency: 'EUR', - value: '57.27', + { + type: 'digital', + sku: '5702015594028', + name: 'LEGO 42056 Porsche 911 GT3 RS', + productUrl: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', + imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', + quantity: 1, + vatRate: '21.00', + unitPrice: { + currency: 'EUR', + value: '329.99', + }, + totalAmount: { + currency: 'EUR', + value: '329.99', + }, + vatAmount: { + currency: 'EUR', + value: '57.27', + }, }, - }, - ], - }); + ], + }); - testOrder(order, 'ord_pbjz8x'); + testOrder(order, 'ord_pbjz8x'); + }); }); -test('getOrder', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('getOrder', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x')).twice(); - networkMocker.intercept('GET', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x')).twice(); + const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz8x'); - const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_pbjz8x'); - - testOrder(order, 'ord_pbjz8x'); + testOrder(order, 'ord_pbjz8x'); + }); }); -test('getOrderIncludingPayments', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/orders/ord_kEn1PlbGa?embed=payments', 200, { - resource: 'order', - id: 'ord_kEn1PlbGa', - profileId: 'pfl_URR55HPMGx', - method: 'klarnapaylater', - amount: { - value: '1027.99', - currency: 'EUR', - }, - status: 'created', - isCancelable: true, - metadata: null, - createdAt: '2018-08-02T09:29:56+00:00', - expiresAt: '2018-08-30T09:29:56+00:00', - mode: 'live', - locale: 'nl_NL', - billingAddress: { - organizationName: 'Mollie B.V.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - orderNumber: '18475', - shippingAddress: { - organizationName: 'Mollie B.V.', - streetAndNumber: 'Keizersgracht 313', - postalCode: '1016 EE', - city: 'Amsterdam', - country: 'nl', - givenName: 'Luke', - familyName: 'Skywalker', - email: 'luke@skywalker.com', - }, - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - lines: [ - { - resource: 'orderline', - id: 'odl_dgtxyl', - orderId: 'ord_pbjz8x', - name: 'LEGO 42083 Bugatti Chiron', - sku: '5702016116977', - type: 'physical', - status: 'created', - metadata: null, - isCancelable: false, - quantity: 2, - quantityShipped: 0, - amountShipped: { - value: '0.00', - currency: 'EUR', - }, - quantityRefunded: 0, - amountRefunded: { - value: '0.00', - currency: 'EUR', - }, - quantityCanceled: 0, - amountCanceled: { - value: '0.00', - currency: 'EUR', - }, - shippableQuantity: 0, - refundableQuantity: 0, - cancelableQuantity: 0, - unitPrice: { - value: '399.00', - currency: 'EUR', - }, - vatRate: '21.00', - vatAmount: { - value: '121.14', - currency: 'EUR', - }, - discountAmount: { - value: '100.00', - currency: 'EUR', - }, - totalAmount: { - value: '698.00', - currency: 'EUR', - }, - createdAt: '2018-08-02T09:29:56+00:00', - _links: { - productUrl: { - href: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', - type: 'text/html', +test('getOrderIncludingPayments', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders/ord_kEn1PlbGa?embed=payments', 200, { + resource: 'order', + id: 'ord_kEn1PlbGa', + profileId: 'pfl_URR55HPMGx', + method: 'klarnapaylater', + amount: { + value: '1027.99', + currency: 'EUR', + }, + status: 'created', + isCancelable: true, + metadata: null, + createdAt: '2018-08-02T09:29:56+00:00', + expiresAt: '2018-08-30T09:29:56+00:00', + mode: 'live', + locale: 'nl_NL', + billingAddress: { + organizationName: 'Mollie B.V.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + orderNumber: '18475', + shippingAddress: { + organizationName: 'Mollie B.V.', + streetAndNumber: 'Keizersgracht 313', + postalCode: '1016 EE', + city: 'Amsterdam', + country: 'nl', + givenName: 'Luke', + familyName: 'Skywalker', + email: 'luke@skywalker.com', + }, + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + lines: [ + { + resource: 'orderline', + id: 'odl_dgtxyl', + orderId: 'ord_pbjz8x', + name: 'LEGO 42083 Bugatti Chiron', + sku: '5702016116977', + type: 'physical', + status: 'created', + metadata: null, + isCancelable: false, + quantity: 2, + quantityShipped: 0, + amountShipped: { + value: '0.00', + currency: 'EUR', }, - imageUrl: { - href: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', - type: 'text/html', + quantityRefunded: 0, + amountRefunded: { + value: '0.00', + currency: 'EUR', }, - }, - }, - { - resource: 'orderline', - id: 'odl_jp31jz', - orderId: 'ord_pbjz8x', - name: 'LEGO 42056 Porsche 911 GT3 RS', - sku: '5702015594028', - type: 'physical', - status: 'created', - metadata: null, - isCancelable: false, - quantity: 1, - quantityShipped: 0, - amountShipped: { - value: '0.00', - currency: 'EUR', - }, - quantityRefunded: 0, - amountRefunded: { - value: '0.00', - currency: 'EUR', - }, - quantityCanceled: 0, - amountCanceled: { - value: '0.00', - currency: 'EUR', - }, - shippableQuantity: 0, - refundableQuantity: 0, - cancelableQuantity: 0, - unitPrice: { - value: '329.99', - currency: 'EUR', - }, - vatRate: '21.00', - vatAmount: { - value: '57.27', - currency: 'EUR', - }, - totalAmount: { - value: '329.99', - currency: 'EUR', - }, - createdAt: '2018-08-02T09:29:56+00:00', - _links: { - productUrl: { - href: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', - type: 'text/html', + quantityCanceled: 0, + amountCanceled: { + value: '0.00', + currency: 'EUR', }, - imageUrl: { - href: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', - type: 'text/html', + shippableQuantity: 0, + refundableQuantity: 0, + cancelableQuantity: 0, + unitPrice: { + value: '399.00', + currency: 'EUR', }, - }, - }, - ], - _embedded: { - payments: [ - { - resource: 'payment', - id: 'tr_ncaPcAhuUV', - mode: 'live', - createdAt: '2018-09-07T12:00:05+00:00', - amount: { - value: '1027.99', + vatRate: '21.00', + vatAmount: { + value: '121.14', currency: 'EUR', }, - description: 'Order #1337 (Lego cars)', - method: null, + discountAmount: { + value: '100.00', + currency: 'EUR', + }, + totalAmount: { + value: '698.00', + currency: 'EUR', + }, + createdAt: '2018-08-02T09:29:56+00:00', + _links: { + productUrl: { + href: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', + type: 'text/html', + }, + imageUrl: { + href: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', + type: 'text/html', + }, + }, + }, + { + resource: 'orderline', + id: 'odl_jp31jz', + orderId: 'ord_pbjz8x', + name: 'LEGO 42056 Porsche 911 GT3 RS', + sku: '5702015594028', + type: 'physical', + status: 'created', metadata: null, - status: 'open', isCancelable: false, - locale: 'nl_NL', - profileId: 'pfl_URR55HPMGx', - orderId: 'ord_kEn1PlbGa', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', + quantity: 1, + quantityShipped: 0, + amountShipped: { + value: '0.00', + currency: 'EUR', + }, + quantityRefunded: 0, + amountRefunded: { + value: '0.00', + currency: 'EUR', + }, + quantityCanceled: 0, + amountCanceled: { + value: '0.00', + currency: 'EUR', + }, + shippableQuantity: 0, + refundableQuantity: 0, + cancelableQuantity: 0, + unitPrice: { + value: '329.99', + currency: 'EUR', + }, + vatRate: '21.00', + vatAmount: { + value: '57.27', + currency: 'EUR', + }, + totalAmount: { + value: '329.99', + currency: 'EUR', + }, + createdAt: '2018-08-02T09:29:56+00:00', _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', - type: 'application/hal+json', - }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/ncaPcAhuUV', + productUrl: { + href: 'https://shop.lego.com/nl-NL/Porsche-911-GT3-RS-42056', type: 'text/html', }, - order: { - href: 'https://api.mollie.com/v2/orders/ord_kEn1PlbGa', - type: 'application/hal+json', + imageUrl: { + href: 'https://sh-s7-live-s.legocdn.com/is/image/LEGO/42056?$PDPDefault$', + type: 'text/html', }, }, }, ], - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/orders/ord_pbjz8x', - type: 'application/hal+json', - }, - checkout: { - href: 'https://www.mollie.com/payscreen/order/checkout/pbjz8x', - type: 'text/html', + _embedded: { + payments: [ + { + resource: 'payment', + id: 'tr_ncaPcAhuUV', + mode: 'live', + createdAt: '2018-09-07T12:00:05+00:00', + amount: { + value: '1027.99', + currency: 'EUR', + }, + description: 'Order #1337 (Lego cars)', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + locale: 'nl_NL', + profileId: 'pfl_URR55HPMGx', + orderId: 'ord_kEn1PlbGa', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/ncaPcAhuUV', + type: 'text/html', + }, + order: { + href: 'https://api.mollie.com/v2/orders/ord_kEn1PlbGa', + type: 'application/hal+json', + }, + }, + }, + ], }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/orders-api/get-order', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/orders/ord_pbjz8x', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/order/checkout/pbjz8x', + type: 'text/html', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/orders-api/get-order', + type: 'text/html', + }, }, - }, - }).twice(); - - const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_kEn1PlbGa', { embed: ['payments'] }); - - expect(order.id).toBe('ord_kEn1PlbGa'); - - const { payments } = order._embedded; - - const payment = payments[0]; - expect(payment.id).toBe('tr_ncaPcAhuUV'); - expect(payment.createdAt).toBe('2018-09-07T12:00:05+00:00'); - expect(payment.amount).toEqual({ value: '1027.99', currency: 'EUR' }); - expect(payment.description).toBe('Order #1337 (Lego cars)'); - expect(payment.method).toBeNull(); - expect(payment.metadata).toBeNull(); - expect(payment.status).toBe('open'); - expect(payment.isCancelable).toBe(false); - expect(payment.locale).toBe('nl_NL'); - expect(payment.profileId).toBe('pfl_URR55HPMGx'); - expect(payment.orderId).toBe('ord_kEn1PlbGa'); - expect(payment.sequenceType).toBe('oneoff'); - expect(payment.redirectUrl).toBe('https://example.org/redirect'); - expect(payment.cancelUrl).toBe('https://example.org/cancel'); - expect(payment._links.self).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', - type: 'application/hal+json', - }); - expect(payment._links.checkout).toEqual({ - href: 'https://www.mollie.com/payscreen/select-method/ncaPcAhuUV', - type: 'text/html', - }); - expect(payment._links.order).toEqual({ - href: 'https://api.mollie.com/v2/orders/ord_kEn1PlbGa', - type: 'application/hal+json', + }).twice(); + + const order = await bluster(mollieClient.orders.get.bind(mollieClient.orders))('ord_kEn1PlbGa', { embed: ['payments'] }); + + expect(order.id).toBe('ord_kEn1PlbGa'); + + const { payments } = order._embedded; + + const payment = payments[0]; + expect(payment.id).toBe('tr_ncaPcAhuUV'); + expect(payment.createdAt).toBe('2018-09-07T12:00:05+00:00'); + expect(payment.amount).toEqual({ value: '1027.99', currency: 'EUR' }); + expect(payment.description).toBe('Order #1337 (Lego cars)'); + expect(payment.method).toBeNull(); + expect(payment.metadata).toBeNull(); + expect(payment.status).toBe('open'); + expect(payment.isCancelable).toBe(false); + expect(payment.locale).toBe('nl_NL'); + expect(payment.profileId).toBe('pfl_URR55HPMGx'); + expect(payment.orderId).toBe('ord_kEn1PlbGa'); + expect(payment.sequenceType).toBe('oneoff'); + expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); + expect(payment._links.self).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', + type: 'application/hal+json', + }); + expect(payment._links.checkout).toEqual({ + href: 'https://www.mollie.com/payscreen/select-method/ncaPcAhuUV', + type: 'text/html', + }); + expect(payment._links.order).toEqual({ + href: 'https://api.mollie.com/v2/orders/ord_kEn1PlbGa', + type: 'application/hal+json', + }); }); }); -test('listOrders', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/orders', 200, { - count: 3, - _embedded: { - orders: [composeOrderResponse('ord_pbjz1x'), composeOrderResponse('ord_pbjz2y'), composeOrderResponse('ord_pbjz3z')], - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/orders', - type: 'application/hal+json', +test('listOrders', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders', 200, { + count: 3, + _embedded: { + orders: [composeOrderResponse('ord_pbjz1x'), composeOrderResponse('ord_pbjz2y'), composeOrderResponse('ord_pbjz3z')], }, - previous: null, - next: { - href: 'https://api.mollie.com/v2/orders?from=ord_stTC2WHAuS', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/orders-api/list-orders', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/orders', + type: 'application/hal+json', + }, + previous: null, + next: { + href: 'https://api.mollie.com/v2/orders?from=ord_stTC2WHAuS', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/orders-api/list-orders', + type: 'text/html', + }, }, - }, - }).twice(); - - const orders = await mollieClient.orders.page(); - - expect(orders.length).toBe(3); - - expect(orders.links.previous).toBeNull(); - expect(orders.links.self).toEqual({ - href: 'https://api.mollie.com/v2/orders', - type: 'application/hal+json', - }); - expect(orders.links.next).toEqual({ - href: 'https://api.mollie.com/v2/orders?from=ord_stTC2WHAuS', - type: 'application/hal+json', + }).twice(); + + const orders = await mollieClient.orders.page(); + + expect(orders.length).toBe(3); + + expect(orders.links.previous).toBeNull(); + expect(orders.links.self).toEqual({ + href: 'https://api.mollie.com/v2/orders', + type: 'application/hal+json', + }); + expect(orders.links.next).toEqual({ + href: 'https://api.mollie.com/v2/orders?from=ord_stTC2WHAuS', + type: 'application/hal+json', + }); + expect(orders.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/orders-api/list-orders', + type: 'text/html', + }); + + testOrder(orders[0], 'ord_pbjz1x'); + testOrder(orders[1], 'ord_pbjz2y'); + testOrder(orders[2], 'ord_pbjz3z'); }); - expect(orders.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/orders-api/list-orders', - type: 'text/html', - }); - - testOrder(orders[0], 'ord_pbjz1x'); - testOrder(orders[1], 'ord_pbjz2y'); - testOrder(orders[2], 'ord_pbjz3z'); }); -test('cancelOrder', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('DELETE', '/orders/ord_pbjz1x', 200, composeOrderResponse('ord_pbjz1x', 'canceled')).twice(); +test('cancelOrder', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('DELETE', '/orders/ord_pbjz1x', 200, composeOrderResponse('ord_pbjz1x', 'canceled')).twice(); - const order = await bluster(mollieClient.orders.cancel.bind(mollieClient.orders))('ord_pbjz1x'); + const order = await bluster(mollieClient.orders.cancel.bind(mollieClient.orders))('ord_pbjz1x'); - testOrder(order, 'ord_pbjz1x', 'canceled'); + testOrder(order, 'ord_pbjz1x', 'canceled'); + }); }); -test('updateOrder', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('PATCH', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x', 'created', '16738')).twice(); +test('updateOrder', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('PATCH', '/orders/ord_pbjz8x', 200, composeOrderResponse('ord_pbjz8x', 'created', '16738')).twice(); + + const order = await bluster(mollieClient.orders.update.bind(mollieClient.orders))('ord_pbjz8x', { + orderNumber: '16738', + billingAddress: { + organizationName: 'Organization Name LTD.', + streetAndNumber: 'Keizersgracht 313', + city: 'Amsterdam', + region: 'Noord-Holland', + postalCode: '1234AB', + country: 'NL', + title: 'Dhr', + givenName: 'Piet', + familyName: 'Mondriaan', + email: 'piet@mondriaan.com', + phone: '+31208202070', + }, + }); - const order = await bluster(mollieClient.orders.update.bind(mollieClient.orders))('ord_pbjz8x', { - orderNumber: '16738', - billingAddress: { - organizationName: 'Organization Name LTD.', - streetAndNumber: 'Keizersgracht 313', - city: 'Amsterdam', - region: 'Noord-Holland', - postalCode: '1234AB', - country: 'NL', - title: 'Dhr', - givenName: 'Piet', - familyName: 'Mondriaan', - email: 'piet@mondriaan.com', - phone: '+31208202070', - }, + testOrder(order, 'ord_pbjz8x', 'created', '16738'); }); - - testOrder(order, 'ord_pbjz8x', 'created', '16738'); }); diff --git a/tests/unit/resources/orders/payments.test.ts b/tests/unit/resources/orders/payments.test.ts index babe2c47..c83422cb 100644 --- a/tests/unit/resources/orders/payments.test.ts +++ b/tests/unit/resources/orders/payments.test.ts @@ -57,16 +57,15 @@ function testPayment(payment, paymentId, paymentStatus = 'open') { }); } -test('createOrderPayment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('createOrderPayment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/payments', 201, composePaymentResponse('tr_WDqYK6vllg', 'ord_stTC2WHAuS')).twice(); - networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/payments', 201, composePaymentResponse('tr_WDqYK6vllg', 'ord_stTC2WHAuS')).twice(); + const payment = await bluster(mollieClient.orderPayments.create.bind(mollieClient.orderPayments))({ + orderId: 'ord_stTC2WHAuS', + method: 'banktransfer', + }); - const payment = await bluster(mollieClient.orderPayments.create.bind(mollieClient.orderPayments))({ - orderId: 'ord_stTC2WHAuS', - method: 'banktransfer', + testPayment(payment, 'tr_WDqYK6vllg'); }); - - testPayment(payment, 'tr_WDqYK6vllg'); }); diff --git a/tests/unit/resources/orders/refunds.test.ts b/tests/unit/resources/orders/refunds.test.ts index 783f63e6..fd2dc5e1 100644 --- a/tests/unit/resources/orders/refunds.test.ts +++ b/tests/unit/resources/orders/refunds.test.ts @@ -87,121 +87,118 @@ function testRefund(refund, refundId, refundStatus = 'pending') { }); } -test('createPartialOrderRefund', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('createPartialOrderRefund', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); - networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); + const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [{ id: 'odl_dgtxyl', quantity: 1 }] }); - const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [{ id: 'odl_dgtxyl', quantity: 1 }] }); - - testRefund(refund, 're_4qqhO89gsT'); + testRefund(refund, 're_4qqhO89gsT'); + }); }); -test('createCompleteOrderRefund', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('createCompleteOrderRefund', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); - networkMocker.intercept('POST', '/orders/ord_stTC2WHAuS/refunds', 201, composeRefundResponse('re_4qqhO89gsT', 'ord_stTC2WHAuS')).twice(); + const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [] }); - const refund = await bluster(mollieClient.orderRefunds.create.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS', lines: [] }); - - testRefund(refund, 're_4qqhO89gsT'); + testRefund(refund, 're_4qqhO89gsT'); + }); }); -test('listOrderRefunds', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/orders/ord_stTC2WHAuS/refunds', 200, { - count: 1, - _embedded: { - refunds: [ - { - resource: 'refund', - id: 're_4qqhO89gsT', - amount: { - currency: 'EUR', - value: '698.00', - }, - status: 'pending', - createdAt: '2018-03-19T12:33:37+00:00', - description: 'Item not in stock, refunding', - paymentId: 'tr_WDqYK6vllg', - orderId: 'ord_pbjz8x', - lines: [ - { - resource: 'orderline', - id: 'odl_dgtxyl', - orderId: 'ord_pbjz8x', - name: 'LEGO 42083 Bugatti Chiron', - productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', - imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', - sku: '5702016116977', - type: 'physical', - status: 'refunded', - quantity: 2, - unitPrice: { - value: '399.00', - currency: 'EUR', +test('listOrderRefunds', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders/ord_stTC2WHAuS/refunds', 200, { + count: 1, + _embedded: { + refunds: [ + { + resource: 'refund', + id: 're_4qqhO89gsT', + amount: { + currency: 'EUR', + value: '698.00', + }, + status: 'pending', + createdAt: '2018-03-19T12:33:37+00:00', + description: 'Item not in stock, refunding', + paymentId: 'tr_WDqYK6vllg', + orderId: 'ord_pbjz8x', + lines: [ + { + resource: 'orderline', + id: 'odl_dgtxyl', + orderId: 'ord_pbjz8x', + name: 'LEGO 42083 Bugatti Chiron', + productUrl: 'https://shop.lego.com/nl-NL/Bugatti-Chiron-42083', + imageUrl: 'https://sh-s7-live-s.legocdn.com/is/image//LEGO/42083_alt1?$main$', + sku: '5702016116977', + type: 'physical', + status: 'refunded', + quantity: 2, + unitPrice: { + value: '399.00', + currency: 'EUR', + }, + vatRate: '21.00', + vatAmount: { + value: '121.14', + currency: 'EUR', + }, + discountAmount: { + value: '100.00', + currency: 'EUR', + }, + totalAmount: { + value: '698.00', + currency: 'EUR', + }, + createdAt: '2018-08-02T09:29:56+00:00', }, - vatRate: '21.00', - vatAmount: { - value: '121.14', - currency: 'EUR', + ], + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_WDqYK6vllg/refunds/re_4qqhO89gsT', + type: 'application/hal+json', }, - discountAmount: { - value: '100.00', - currency: 'EUR', + payment: { + href: 'https://api.mollie.com/v2/payments/tr_WDqYK6vllg', + type: 'application/hal+json', }, - totalAmount: { - value: '698.00', - currency: 'EUR', + order: { + href: 'https://api.mollie.com/v2/orders/ord_pbjz8x', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', + type: 'text/html', }, - createdAt: '2018-08-02T09:29:56+00:00', - }, - ], - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_WDqYK6vllg/refunds/re_4qqhO89gsT', - type: 'application/hal+json', - }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_WDqYK6vllg', - type: 'application/hal+json', - }, - order: { - href: 'https://api.mollie.com/v2/orders/ord_pbjz8x', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', - type: 'text/html', }, }, - }, - ], - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/refunds?limit=5', - type: 'application/hal+json', + ], }, - previous: null, - next: { - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/refunds?from=re_APBiGPH2vV&limit=5', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/orders-api/list-order-refunds', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/refunds?limit=5', + type: 'application/hal+json', + }, + previous: null, + next: { + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/refunds?from=re_APBiGPH2vV&limit=5', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/orders-api/list-order-refunds', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const refunds = await bluster(mollieClient.orderRefunds.page.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS' }); + const refunds = await bluster(mollieClient.orderRefunds.page.bind(mollieClient.orderRefunds))({ orderId: 'ord_stTC2WHAuS' }); - expect(refunds.length).toBe(1); + expect(refunds.length).toBe(1); - testRefund(refunds[0], 're_4qqhO89gsT'); + testRefund(refunds[0], 're_4qqhO89gsT'); + }); }); diff --git a/tests/unit/resources/orders/shipments.test.ts b/tests/unit/resources/orders/shipments.test.ts index 67fc2fa7..8991ec98 100644 --- a/tests/unit/resources/orders/shipments.test.ts +++ b/tests/unit/resources/orders/shipments.test.ts @@ -135,33 +135,31 @@ function testShipment(shipment, shipmentId, orderId) { expect(line1.totalAmount).toEqual({ value: '329.99', currency: 'EUR' }); } -test('createShipment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('createShipment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/orders/ord_pbjz8x/shipments', 201, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); - networkMocker.intercept('POST', '/orders/ord_pbjz8x/shipments', 201, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); + const shipment = await bluster(mollieClient.orderShipments.create.bind(mollieClient.orderShipments))({ + orderId: 'ord_pbjz8x', + lines: [ + { + id: 'odl_dgtxyl', + quantity: 1, + }, + { id: 'odl_jp31jz' }, + ], + }); - const shipment = await bluster(mollieClient.orderShipments.create.bind(mollieClient.orderShipments))({ - orderId: 'ord_pbjz8x', - lines: [ - { - id: 'odl_dgtxyl', - quantity: 1, - }, - { id: 'odl_jp31jz' }, - ], + testShipment(shipment, 'shp_3wmsgCJN4U', 'ord_pbjz8x'); }); - - testShipment(shipment, 'shp_3wmsgCJN4U', 'ord_pbjz8x'); }); -test('getShipment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('getShipment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/orders/ord_pbjz8x/shipments/shp_3wmsgCJN4U', 200, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); - networkMocker.intercept('GET', '/orders/ord_pbjz8x/shipments/shp_3wmsgCJN4U', 200, composeShipmentResponse('shp_3wmsgCJN4U', 'ord_pbjz8x')).twice(); + const shipment = await bluster(mollieClient.orderShipments.get.bind(mollieClient.orderShipments))('shp_3wmsgCJN4U', { orderId: 'ord_pbjz8x' }); - const shipment = await bluster(mollieClient.orderShipments.get.bind(mollieClient.orderShipments))('shp_3wmsgCJN4U', { orderId: 'ord_pbjz8x' }); - - testShipment(shipment, 'shp_3wmsgCJN4U', 'ord_pbjz8x'); + testShipment(shipment, 'shp_3wmsgCJN4U', 'ord_pbjz8x'); + }); }); diff --git a/tests/unit/resources/organizations.test.ts b/tests/unit/resources/organizations.test.ts index f3279b78..8ec2e58e 100644 --- a/tests/unit/resources/organizations.test.ts +++ b/tests/unit/resources/organizations.test.ts @@ -49,24 +49,22 @@ function testOrganization(organization) { }); } -test('getOrganization', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('getOrganization', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/organizations/org_12345678', 200, response).twice(); - networkMocker.intercept('GET', '/organizations/org_12345678', 200, response).twice(); + const organization = await bluster(mollieClient.organizations.get.bind(mollieClient.organizations))('org_12345678'); - const organization = await bluster(mollieClient.organizations.get.bind(mollieClient.organizations))('org_12345678'); - - testOrganization(organization); + testOrganization(organization); + }); }); -test('getCurrentOrganization', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('getCurrentOrganization', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/organizations/me', 200, response).twice(); - networkMocker.intercept('GET', '/organizations/me', 200, response).twice(); + const organization = await bluster(mollieClient.organizations.getCurrent.bind(mollieClient.organizations))(); - const organization = await bluster(mollieClient.organizations.getCurrent.bind(mollieClient.organizations))(); - - testOrganization(organization); + testOrganization(organization); + }); }); diff --git a/tests/unit/resources/payments.test.ts b/tests/unit/resources/payments.test.ts index b26f09e4..d1de63d4 100644 --- a/tests/unit/resources/payments.test.ts +++ b/tests/unit/resources/payments.test.ts @@ -1,375 +1,371 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('createPayment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/payments', 201, { - resource: 'payment', - id: 'tr_44aKxzEbr8', - mode: 'test', - createdAt: '2018-03-13T14:02:29+00:00', - amount: { - value: '20.00', - currency: 'EUR', - }, - description: 'My first API payment', - method: null, - metadata: { - order_id: '1234', - }, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-13T14:17:29+00:00', - details: null, - profileId: 'pfl_2A1gacu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', +test('createPayment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/payments', 201, { + resource: 'payment', + id: 'tr_44aKxzEbr8', + mode: 'test', + createdAt: '2018-03-13T14:02:29+00:00', + amount: { + value: '20.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', - type: 'text/html', + description: 'My first API payment', + method: null, + metadata: { + order_id: '1234', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/payments-api/create-payment', - type: 'text/html', + status: 'open', + isCancelable: false, + expiresAt: '2018-03-13T14:17:29+00:00', + details: null, + profileId: 'pfl_2A1gacu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', + type: 'text/html', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/payments-api/create-payment', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const payment = await bluster(mollieClient.payments.create.bind(mollieClient.payments))({ - amount: { - currency: 'EUR', - value: '20.00', - }, - description: 'My first API payment', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - metadata: { - order_id: '1234', - }, - }); + const payment = await bluster(mollieClient.payments.create.bind(mollieClient.payments))({ + amount: { + currency: 'EUR', + value: '20.00', + }, + description: 'My first API payment', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + metadata: { + order_id: '1234', + }, + }); - expect(payment.id).toBe('tr_44aKxzEbr8'); - expect(payment.mode).toBe('test'); - expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); + expect(payment.id).toBe('tr_44aKxzEbr8'); + expect(payment.mode).toBe('test'); + expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); - expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); + expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); - expect(payment.description).toBe('My first API payment'); - expect(payment.method).toBeNull(); - expect(payment.metadata).toEqual({ order_id: '1234' }); - expect(payment.status).toBe('open'); - expect(payment.isCancelable).toBe(false); - expect(payment.expiresAt).toBe('2018-03-13T14:17:29+00:00'); - expect(payment.details).toBeNull(); - expect(payment.profileId).toBe('pfl_2A1gacu42V'); - expect(payment.sequenceType).toBe('oneoff'); - expect(payment.redirectUrl).toBe('https://example.org/redirect'); - expect(payment.cancelUrl).toBe('https://example.org/cancel'); - expect(payment.webhookUrl).toBe('https://example.org/webhook'); + expect(payment.description).toBe('My first API payment'); + expect(payment.method).toBeNull(); + expect(payment.metadata).toEqual({ order_id: '1234' }); + expect(payment.status).toBe('open'); + expect(payment.isCancelable).toBe(false); + expect(payment.expiresAt).toBe('2018-03-13T14:17:29+00:00'); + expect(payment.details).toBeNull(); + expect(payment.profileId).toBe('pfl_2A1gacu42V'); + expect(payment.sequenceType).toBe('oneoff'); + expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); + expect(payment.webhookUrl).toBe('https://example.org/webhook'); - expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); + expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); - expect(payment._links.checkout).toEqual({ href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', type: 'text/html' }); + expect(payment._links.checkout).toEqual({ href: 'https://www.mollie.com/payscreen/select-method/44aKxzEbr8', type: 'text/html' }); - expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/create-payment', type: 'text/html' }); + expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/create-payment', type: 'text/html' }); + }); }); -test('updatePayment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('PATCH', '/payments/tr_7UhSN1zuXS', 200, { - resource: 'payment', - id: 'tr_7UhSN1zuXS', - mode: 'test', - createdAt: '2018-03-20T09:13:37+00:00', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order #98765', - method: null, - metadata: { - order_id: '98765', - }, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-20T09:28:37+00:00', - details: null, - profileId: 'pfl_QkEhN94Ba', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/webshop/order/98765/', - webhookUrl: 'https://example.org/webshop/payments/webhook/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', - type: 'application/json', +test('updatePayment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('PATCH', '/payments/tr_7UhSN1zuXS', 200, { + resource: 'payment', + id: 'tr_7UhSN1zuXS', + mode: 'test', + createdAt: '2018-03-20T09:13:37+00:00', + amount: { + value: '10.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', - type: 'text/html', + description: 'Order #98765', + method: null, + metadata: { + order_id: '98765', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/payments-api/update-payment', - type: 'text/html', + status: 'open', + isCancelable: false, + expiresAt: '2018-03-20T09:28:37+00:00', + details: null, + profileId: 'pfl_QkEhN94Ba', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/webshop/order/98765/', + webhookUrl: 'https://example.org/webshop/payments/webhook/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + type: 'application/json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', + type: 'text/html', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/payments-api/update-payment', + type: 'text/html', + }, }, - }, - }).twice(); - - const payment = await bluster(mollieClient.payments.update).bind(mollieClient.payments)('tr_7UhSN1zuXS', { - description: 'Order #98765', - redirectUrl: 'https://example.org/webshop/order/98765/', - webhookUrl: 'https://example.org/webshop/payments/webhook/', - metadata: { order_id: '98765' }, - }); - - expect(payment.id).toBe('tr_7UhSN1zuXS'); - - expect(payment.description).toBe('Order #98765'); - expect(payment.redirectUrl).toBe('https://example.org/webshop/order/98765/'); - expect(payment.webhookUrl).toBe('https://example.org/webshop/payments/webhook/'); - expect(payment.metadata).toEqual({ order_id: '98765' }); - expect(payment.sequenceType).toBe('oneoff'); - expect(payment.profileId).toBe('pfl_QkEhN94Ba'); - expect(payment.details).toBeNull(); - - expect(payment._links.self).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', - type: 'application/json', - }); - - expect(payment._links.checkout).toEqual({ - href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', - type: 'text/html', - }); - - expect(payment._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/payments-api/update-payment', - type: 'text/html', + }).twice(); + + const payment = await bluster(mollieClient.payments.update).bind(mollieClient.payments)('tr_7UhSN1zuXS', { + description: 'Order #98765', + redirectUrl: 'https://example.org/webshop/order/98765/', + webhookUrl: 'https://example.org/webshop/payments/webhook/', + metadata: { order_id: '98765' }, + }); + + expect(payment.id).toBe('tr_7UhSN1zuXS'); + + expect(payment.description).toBe('Order #98765'); + expect(payment.redirectUrl).toBe('https://example.org/webshop/order/98765/'); + expect(payment.webhookUrl).toBe('https://example.org/webshop/payments/webhook/'); + expect(payment.metadata).toEqual({ order_id: '98765' }); + expect(payment.sequenceType).toBe('oneoff'); + expect(payment.profileId).toBe('pfl_QkEhN94Ba'); + expect(payment.details).toBeNull(); + + expect(payment._links.self).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + type: 'application/json', + }); + + expect(payment._links.checkout).toEqual({ + href: 'https://www.mollie.com/payscreen/select-method/7UhSN1zuXS', + type: 'text/html', + }); + + expect(payment._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/payments-api/update-payment', + type: 'text/html', + }); }); }); -test('getPayment', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { - resource: 'payment', - id: 'tr_44aKxzEbr8', - mode: 'test', - createdAt: '2018-03-13T14:02:29+00:00', - amount: { - value: '20.00', - currency: 'EUR', - }, - description: 'My first API payment', - method: 'ideal', - metadata: { - order_id: '1234', - }, - status: 'paid', - paidAt: '2018-03-19T12:18:35+00:00', - amountRefunded: { - value: '0.00', - currency: 'EUR', - }, - amountRemaining: { - value: '20.00', - currency: 'EUR', - }, - details: { - consumerName: 'T. TEST', - consumerAccount: 'NL17RABO0213698412', - consumerBic: 'TESTNL99', - }, - locale: 'nl_NL', - countryCode: 'NL', - profileId: 'pfl_2A1gacu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://example.org/redirect', - cancelUrl: 'https://example.org/cancel', - webhookUrl: 'https://example.org/webhook', - settlementAmount: { - value: '20.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', +test('getPayment', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8', 200, { + resource: 'payment', + id: 'tr_44aKxzEbr8', + mode: 'test', + createdAt: '2018-03-13T14:02:29+00:00', + amount: { + value: '20.00', + currency: 'EUR', + }, + description: 'My first API payment', + method: 'ideal', + metadata: { + order_id: '1234', + }, + status: 'paid', + paidAt: '2018-03-19T12:18:35+00:00', + amountRefunded: { + value: '0.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', - type: 'text/html', + amountRemaining: { + value: '20.00', + currency: 'EUR', }, - }, - }).twice(); + details: { + consumerName: 'T. TEST', + consumerAccount: 'NL17RABO0213698412', + consumerBic: 'TESTNL99', + }, + locale: 'nl_NL', + countryCode: 'NL', + profileId: 'pfl_2A1gacu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', + webhookUrl: 'https://example.org/webhook', + settlementAmount: { + value: '20.00', + currency: 'EUR', + }, + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', + type: 'text/html', + }, + }, + }).twice(); - const payment = await bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); + const payment = await bluster(mollieClient.payments.get.bind(mollieClient.payments))('tr_44aKxzEbr8'); - expect(payment.id).toBe('tr_44aKxzEbr8'); - expect(payment.mode).toBe('test'); - expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); + expect(payment.id).toBe('tr_44aKxzEbr8'); + expect(payment.mode).toBe('test'); + expect(payment.createdAt).toBe('2018-03-13T14:02:29+00:00'); - expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); + expect(payment.amount).toEqual({ value: '20.00', currency: 'EUR' }); - expect(payment.description).toBe('My first API payment'); - expect(payment.method).toBe('ideal'); - expect(payment.metadata).toEqual({ order_id: '1234' }); - expect(payment.status).toBe('paid'); + expect(payment.description).toBe('My first API payment'); + expect(payment.method).toBe('ideal'); + expect(payment.metadata).toEqual({ order_id: '1234' }); + expect(payment.status).toBe('paid'); - expect(payment.amountRefunded).toEqual({ value: '0.00', currency: 'EUR' }); - expect(payment.amountRemaining).toEqual({ value: '20.00', currency: 'EUR' }); - expect(payment.details).toEqual({ consumerName: 'T. TEST', consumerAccount: 'NL17RABO0213698412', consumerBic: 'TESTNL99' }); - expect(payment.profileId).toBe('pfl_2A1gacu42V'); - expect(payment.sequenceType).toBe('oneoff'); - expect(payment.redirectUrl).toBe('https://example.org/redirect'); - expect(payment.cancelUrl).toBe('https://example.org/cancel'); - expect(payment.webhookUrl).toBe('https://example.org/webhook'); + expect(payment.amountRefunded).toEqual({ value: '0.00', currency: 'EUR' }); + expect(payment.amountRemaining).toEqual({ value: '20.00', currency: 'EUR' }); + expect(payment.details).toEqual({ consumerName: 'T. TEST', consumerAccount: 'NL17RABO0213698412', consumerBic: 'TESTNL99' }); + expect(payment.profileId).toBe('pfl_2A1gacu42V'); + expect(payment.sequenceType).toBe('oneoff'); + expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); + expect(payment.webhookUrl).toBe('https://example.org/webhook'); - expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); + expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); - expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', type: 'text/html' }); + expect(payment._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/get-payment', type: 'text/html' }); + }); }); -test('listPayments', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/payments?limit=3', 200, { - _embedded: { - payments: [ - { - resource: 'payment', - id: 'tr_admNa2tFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', - }, - description: 'Payment no 1', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_admNa2tFfa', - type: 'application/hal+json', +test('listPayments', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments?limit=3', 200, { + _embedded: { + payments: [ + { + resource: 'payment', + id: 'tr_admNa2tFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/admNa2tFfa', - type: 'text/html', + description: 'Payment no 1', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_admNa2tFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/admNa2tFfa', + type: 'text/html', + }, }, }, - }, - { - resource: 'payment', - id: 'tr_bcaLc7hFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', - }, - description: 'Payment no 2', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_bcaLc7hFfa', - type: 'application/hal+json', + { + resource: 'payment', + id: 'tr_bcaLc7hFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/bcaLc7hFfa', - type: 'text/html', + description: 'Payment no 2', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_bcaLc7hFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/bcaLc7hFfa', + type: 'text/html', + }, }, }, - }, - { - resource: 'payment', - id: 'tr_pslHy1tFfa', - mode: 'test', - createdAt: '2018-03-19T15:00:50+00:00', - amount: { - value: '100.00', - currency: 'EUR', - }, - description: 'Payment no 3', - method: null, - metadata: null, - status: 'open', - isCancelable: false, - expiresAt: '2018-03-19T15:15:50+00:00', - details: null, - locale: 'nl_NL', - profileId: 'pfl_7N5qjbu42V', - sequenceType: 'oneoff', - redirectUrl: 'https://www.example.org/', - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_pslHy1tFfa', - type: 'application/hal+json', + { + resource: 'payment', + id: 'tr_pslHy1tFfa', + mode: 'test', + createdAt: '2018-03-19T15:00:50+00:00', + amount: { + value: '100.00', + currency: 'EUR', }, - checkout: { - href: 'https://www.mollie.com/payscreen/select-method/pslHy1tFfa', - type: 'text/html', + description: 'Payment no 3', + method: null, + metadata: null, + status: 'open', + isCancelable: false, + expiresAt: '2018-03-19T15:15:50+00:00', + details: null, + locale: 'nl_NL', + profileId: 'pfl_7N5qjbu42V', + sequenceType: 'oneoff', + redirectUrl: 'https://www.example.org/', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_pslHy1tFfa', + type: 'application/hal+json', + }, + checkout: { + href: 'https://www.mollie.com/payscreen/select-method/pslHy1tFfa', + type: 'text/html', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/payments-api/list-payments', - type: 'text/html', + ], }, - self: { - href: 'http://api.mollie.com/v2/payments?limit=3', - type: 'application/hal+json', - }, - previous: null, - next: { - href: 'http://api.mollie.com/v2/payments?from=tr_eW8f5kzUkF&limit=3', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/payments-api/list-payments', + type: 'text/html', + }, + self: { + href: 'http://api.mollie.com/v2/payments?limit=3', + type: 'application/hal+json', + }, + previous: null, + next: { + href: 'http://api.mollie.com/v2/payments?from=tr_eW8f5kzUkF&limit=3', + type: 'application/hal+json', + }, }, - }, - count: 3, - }).twice(); + count: 3, + }).twice(); - const payments = await bluster(mollieClient.payments.page.bind(mollieClient.payments))({ limit: 3 }); + const payments = await bluster(mollieClient.payments.page.bind(mollieClient.payments))({ limit: 3 }); - expect(payments.length).toBe(3); + expect(payments.length).toBe(3); - expect(payments.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/list-payments', type: 'text/html' }); + expect(payments.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/payments-api/list-payments', type: 'text/html' }); - expect(payments.links.self).toEqual({ href: 'http://api.mollie.com/v2/payments?limit=3', type: 'application/hal+json' }); + expect(payments.links.self).toEqual({ href: 'http://api.mollie.com/v2/payments?limit=3', type: 'application/hal+json' }); - expect(payments.links.previous).toBeNull(); + expect(payments.links.previous).toBeNull(); - expect(payments.links.next).toEqual({ href: 'http://api.mollie.com/v2/payments?from=tr_eW8f5kzUkF&limit=3', type: 'application/hal+json' }); + expect(payments.links.next).toEqual({ href: 'http://api.mollie.com/v2/payments?from=tr_eW8f5kzUkF&limit=3', type: 'application/hal+json' }); + }); }); diff --git a/tests/unit/resources/payments/captures.test.ts b/tests/unit/resources/payments/captures.test.ts index 81e3658d..17c9e05a 100644 --- a/tests/unit/resources/payments/captures.test.ts +++ b/tests/unit/resources/payments/captures.test.ts @@ -81,56 +81,54 @@ function testCapture(capture) { }); } -test('getCapture', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); +test('getCapture', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT', 200, composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')).twice(); - networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT', 200, composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')).twice(); + const capture = await bluster(mollieClient.paymentCaptures.get.bind(mollieClient.paymentCaptures))('cpt_4qqhO89gsT', { paymentId: 'tr_WDqYK6vllg' }); - const capture = await bluster(mollieClient.paymentCaptures.get.bind(mollieClient.paymentCaptures))('cpt_4qqhO89gsT', { paymentId: 'tr_WDqYK6vllg' }); - - testCapture(capture); + testCapture(capture); + }); }); -test('listCaptures', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures', 200, { - _embedded: { - captures: [composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')], - }, - count: 1, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/captures-api/list-captures', - type: 'text/html', +test('listCaptures', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments/tr_WDqYK6vllg/captures', 200, { + _embedded: { + captures: [composeCaptureResponse('tr_WDqYK6vllg', 'cpt_4qqhO89gsT')], }, - self: { - href: 'https://api.mollie.dev/v2/payments/tr_WDqYK6vllg/captures?limit=50', - type: 'application/hal+json', + count: 1, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/captures-api/list-captures', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.dev/v2/payments/tr_WDqYK6vllg/captures?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const captures = await bluster(mollieClient.paymentCaptures.page.bind(mollieClient.paymentCaptures))({ paymentId: 'tr_WDqYK6vllg' }); + const captures = await bluster(mollieClient.paymentCaptures.page.bind(mollieClient.paymentCaptures))({ paymentId: 'tr_WDqYK6vllg' }); - expect(captures.length).toBe(1); + expect(captures.length).toBe(1); - expect(captures.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/captures-api/list-captures', - type: 'text/html', - }); + expect(captures.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/captures-api/list-captures', + type: 'text/html', + }); - expect(captures.links.self).toEqual({ - href: 'https://api.mollie.dev/v2/payments/tr_WDqYK6vllg/captures?limit=50', - type: 'application/hal+json', - }); + expect(captures.links.self).toEqual({ + href: 'https://api.mollie.dev/v2/payments/tr_WDqYK6vllg/captures?limit=50', + type: 'application/hal+json', + }); - expect(captures.links.previous).toBeNull(); - expect(captures.links.next).toBeNull(); + expect(captures.links.previous).toBeNull(); + expect(captures.links.next).toBeNull(); - testCapture(captures[0]); + testCapture(captures[0]); + }); }); diff --git a/tests/unit/resources/payments/refunds.test.ts b/tests/unit/resources/payments/refunds.test.ts index 315b0034..395291be 100644 --- a/tests/unit/resources/payments/refunds.test.ts +++ b/tests/unit/resources/payments/refunds.test.ts @@ -1,141 +1,139 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; -test('getRefund', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8/refunds/re_PsAvxvLsnm', 200, { - resource: 'refund', - id: 're_PsAvxvLsnm', - amount: { - value: '20.00', - currency: 'EUR', - }, - status: 'pending', - createdAt: '2018-03-19T12:33:37+00:00', - description: 'My first API payment', - paymentId: 'tr_44aKxzEbr8', - settlementAmount: { - value: '-20.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', - type: 'application/hal+json', +test('getRefund', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/payments/tr_44aKxzEbr8/refunds/re_PsAvxvLsnm', 200, { + resource: 'refund', + id: 're_PsAvxvLsnm', + amount: { + value: '20.00', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + status: 'pending', + createdAt: '2018-03-19T12:33:37+00:00', + description: 'My first API payment', + paymentId: 'tr_44aKxzEbr8', + settlementAmount: { + value: '-20.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', + type: 'text/html', + }, }, - }, - }).twice(); - - const refund = await bluster(mollieClient.paymentRefunds.get.bind(mollieClient.paymentRefunds))('re_PsAvxvLsnm', { paymentId: 'tr_44aKxzEbr8' }); - - expect(refund.id).toBe('re_PsAvxvLsnm'); - - expect(refund.amount).toEqual({ - value: '20.00', - currency: 'EUR', - }); + }).twice(); - expect(refund.status).toBe('pending'); - expect(refund.createdAt).toBe('2018-03-19T12:33:37+00:00'); - expect(refund.description).toBe('My first API payment'); - expect(refund.paymentId).toBe('tr_44aKxzEbr8'); + const refund = await bluster(mollieClient.paymentRefunds.get.bind(mollieClient.paymentRefunds))('re_PsAvxvLsnm', { paymentId: 'tr_44aKxzEbr8' }); - expect(refund.settlementAmount).toEqual({ - value: '-20.00', - currency: 'EUR', - }); + expect(refund.id).toBe('re_PsAvxvLsnm'); - expect(refund._links.self).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', - type: 'application/hal+json', - }); + expect(refund.amount).toEqual({ + value: '20.00', + currency: 'EUR', + }); - expect(refund._links.payment).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', - }); + expect(refund.status).toBe('pending'); + expect(refund.createdAt).toBe('2018-03-19T12:33:37+00:00'); + expect(refund.description).toBe('My first API payment'); + expect(refund.paymentId).toBe('tr_44aKxzEbr8'); - expect(refund._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', - type: 'text/html', + expect(refund.settlementAmount).toEqual({ + value: '-20.00', + currency: 'EUR', + }); + + expect(refund._links.self).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', + type: 'application/hal+json', + }); + + expect(refund._links.payment).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }); + + expect(refund._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/refunds-api/get-refund', + type: 'text/html', + }); }); }); -test('createRefund', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('POST', '/payments/tr_44aKxzEbr8/refunds', 201, { - resource: 'refund', - id: 're_PsAvxvLsnm', - amount: { - value: '20.00', - currency: 'EUR', - }, - status: 'pending', - createdAt: '2018-03-19T12:33:37+00:00', - description: 'My first API payment', - paymentId: 'tr_44aKxzEbr8', - settlementAmount: { - value: '-20.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', - type: 'application/hal+json', +test('createRefund', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('POST', '/payments/tr_44aKxzEbr8/refunds', 201, { + resource: 'refund', + id: 're_PsAvxvLsnm', + amount: { + value: '20.00', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + status: 'pending', + createdAt: '2018-03-19T12:33:37+00:00', + description: 'My first API payment', + paymentId: 'tr_44aKxzEbr8', + settlementAmount: { + value: '-20.00', + currency: 'EUR', }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/refunds-api/create-refund', - type: 'text/html', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/refunds-api/create-refund', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const refund = await bluster(mollieClient.paymentRefunds.create.bind(mollieClient.paymentRefunds))({ paymentId: 'tr_44aKxzEbr8', amount: { currency: 'EUR', value: '20.00' } }); + const refund = await bluster(mollieClient.paymentRefunds.create.bind(mollieClient.paymentRefunds))({ paymentId: 'tr_44aKxzEbr8', amount: { currency: 'EUR', value: '20.00' } }); - expect(refund.id).toBe('re_PsAvxvLsnm'); + expect(refund.id).toBe('re_PsAvxvLsnm'); - expect(refund.amount).toEqual({ - value: '20.00', - currency: 'EUR', - }); - - expect(refund.status).toBe('pending'); - expect(refund.createdAt).toBe('2018-03-19T12:33:37+00:00'); - expect(refund.description).toBe('My first API payment'); - expect(refund.paymentId).toBe('tr_44aKxzEbr8'); - - expect(refund.settlementAmount).toEqual({ - value: '-20.00', - currency: 'EUR', - }); + expect(refund.amount).toEqual({ + value: '20.00', + currency: 'EUR', + }); - expect(refund._links.self).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', - type: 'application/hal+json', - }); + expect(refund.status).toBe('pending'); + expect(refund.createdAt).toBe('2018-03-19T12:33:37+00:00'); + expect(refund.description).toBe('My first API payment'); + expect(refund.paymentId).toBe('tr_44aKxzEbr8'); - expect(refund._links.payment).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', - }); - - expect(refund._links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/refunds-api/create-refund', - type: 'text/html', + expect(refund.settlementAmount).toEqual({ + value: '-20.00', + currency: 'EUR', + }); + + expect(refund._links.self).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_Tgxm3amJBT/refunds/re_PmEtpvSsnm', + type: 'application/hal+json', + }); + + expect(refund._links.payment).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }); + + expect(refund._links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/refunds-api/create-refund', + type: 'text/html', + }); }); }); diff --git a/tests/unit/resources/permissions.test.ts b/tests/unit/resources/permissions.test.ts index e2c4bfdc..e364377a 100644 --- a/tests/unit/resources/permissions.test.ts +++ b/tests/unit/resources/permissions.test.ts @@ -11,72 +11,39 @@ function testPermission(permission, id) { expect(permission._links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/permissions-api/get-permission', type: 'text/html' }); } -test('getPermissions', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - await Promise.all( - [ - 'payments.read', - 'payments.write', - 'refunds.read', - 'refunds.write', - 'customers.read', - 'customers.write', - 'mandates.read', - 'mandates.write', - 'subscriptions.read', - 'subscriptions.write', - 'profiles.read', - 'profiles.write', - 'invoices.read', - 'invoices.write', - 'settlements.read', - 'settlements.write', - 'orders.read', - 'orders.write', - 'organizations.read', - 'organizations.write', - ].map(async id => { - networkMocker.intercept('GET', `/permissions/${id}`, 200, { - resource: 'permission', - id, - description: 'Some dummy permission description', - granted: true, - _links: { - self: { - href: `https://api.mollie.com/v2/permissions/${id}`, - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/permissions-api/get-permission', - type: 'text/html', - }, - }, - }).twice(); - - const permission = await bluster(mollieClient.permissions.get.bind(mollieClient.permissions))(id); - - testPermission(permission, id); - }), - ); -}); - -test('listPermissions', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/permissions', 200, { - _embedded: { - permissions: [ - { +test('getPermissions', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + await Promise.all( + [ + 'payments.read', + 'payments.write', + 'refunds.read', + 'refunds.write', + 'customers.read', + 'customers.write', + 'mandates.read', + 'mandates.write', + 'subscriptions.read', + 'subscriptions.write', + 'profiles.read', + 'profiles.write', + 'invoices.read', + 'invoices.write', + 'settlements.read', + 'settlements.write', + 'orders.read', + 'orders.write', + 'organizations.read', + 'organizations.write', + ].map(async id => { + networkMocker.intercept('GET', `/permissions/${id}`, 200, { resource: 'permission', - id: 'payments.write', + id, description: 'Some dummy permission description', granted: true, _links: { self: { - href: 'https://api.mollie.com/v2/permissions/payments.write', + href: `https://api.mollie.com/v2/permissions/${id}`, type: 'application/hal+json', }, documentation: { @@ -84,42 +51,73 @@ test('listPermissions', async () => { type: 'text/html', }, }, - }, - { - resource: 'permission', - id: 'payments.read', - description: 'Some dummy permission description', - granted: true, - _links: { - self: { - href: 'https://api.mollie.com/v2/permissions/payments.read', - type: 'application/hal+json', + }).twice(); + + const permission = await bluster(mollieClient.permissions.get.bind(mollieClient.permissions))(id); + + testPermission(permission, id); + }), + ); + }); +}); + +test('listPermissions', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/permissions', 200, { + _embedded: { + permissions: [ + { + resource: 'permission', + id: 'payments.write', + description: 'Some dummy permission description', + granted: true, + _links: { + self: { + href: 'https://api.mollie.com/v2/permissions/payments.write', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/permissions-api/get-permission', + type: 'text/html', + }, }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/permissions-api/get-permission', - type: 'text/html', + }, + { + resource: 'permission', + id: 'payments.read', + description: 'Some dummy permission description', + granted: true, + _links: { + self: { + href: 'https://api.mollie.com/v2/permissions/payments.read', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/permissions-api/get-permission', + type: 'text/html', + }, }, }, - }, - ], - }, - count: 2, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/permissions-api/list-permissions', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/permissions', - type: 'application/hal+json', + count: 2, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/permissions-api/list-permissions', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/permissions', + type: 'application/hal+json', + }, }, - }, - }).twice(); + }).twice(); - const permissions = await bluster(mollieClient.permissions.list.bind(mollieClient.permissions))(); + const permissions = await bluster(mollieClient.permissions.list.bind(mollieClient.permissions))(); - expect(permissions.length).toBe(2); + expect(permissions.length).toBe(2); - testPermission(permissions[0], 'payments.write'); - testPermission(permissions[1], 'payments.read'); + testPermission(permissions[0], 'payments.write'); + testPermission(permissions[1], 'payments.read'); + }); }); diff --git a/tests/unit/resources/profiles.test.ts b/tests/unit/resources/profiles.test.ts index 0ad64728..187d425b 100644 --- a/tests/unit/resources/profiles.test.ts +++ b/tests/unit/resources/profiles.test.ts @@ -1,93 +1,15 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('getProfile', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - await Promise.all( - ( - [ - ['/profiles/pfl_ahe8z8OPut', bluster(mollieClient.profiles.get.bind(mollieClient.profiles)).bind(undefined, 'pfl_ahe8z8OPut')], - ['/profiles/me', bluster(mollieClient.profiles.getCurrent.bind(mollieClient.profiles))], - ] as [string, () => Promise][] - ).map(async ([url, get]) => { - networkMocker.intercept('GET', url, 200, { - resource: 'profile', - id: 'pfl_ahe8z8OPut', - mode: 'live', - name: 'My website name', - website: 'http://www.mywebsite.com', - email: 'info@mywebsite.com', - phone: '31123456789', - categoryCode: 5399, - status: 'verified', - review: { - status: 'pending', - }, - createdAt: '2016-01-11T13:03:55+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - chargebacks: { - href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - methods: { - href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - payments: { - href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - refunds: { - href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - checkoutPreviewUrl: { - href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', - type: 'text/html', - }, - }, - }).twice(); - - const profile = await get(); - - expect(profile.id).toBe('pfl_ahe8z8OPut'); - expect(profile.mode).toBe('live'); - expect(profile.name).toBe('My website name'); - expect(profile.website).toBe('http://www.mywebsite.com'); - expect(profile.email).toBe('info@mywebsite.com'); - expect(profile.phone).toBe('31123456789'); - expect(profile.categoryCode).toBe(5399); - expect(profile.status).toBe('verified'); - expect(profile.review).toEqual({ status: 'pending' }); - - expect(profile._links.self).toEqual({ href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', type: 'application/hal+json' }); - - expect(profile._links.chargebacks).toEqual({ href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); - - expect(profile._links.methods).toEqual({ href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); - - expect(profile._links.payments).toEqual({ href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); - - expect(profile._links.refunds).toEqual({ href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); - - expect(profile._links.checkoutPreviewUrl).toEqual({ href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', type: 'text/html' }); - }), - ); -}); - -test('listProfiles', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/profiles', 200, { - _embedded: { - profiles: [ - { +test('getProfile', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + await Promise.all( + ( + [ + ['/profiles/pfl_ahe8z8OPut', bluster(mollieClient.profiles.get.bind(mollieClient.profiles)).bind(undefined, 'pfl_ahe8z8OPut')], + ['/profiles/me', bluster(mollieClient.profiles.getCurrent.bind(mollieClient.profiles))], + ] as [string, () => Promise][] + ).map(async ([url, get]) => { + networkMocker.intercept('GET', url, 200, { resource: 'profile', id: 'pfl_ahe8z8OPut', mode: 'live', @@ -127,127 +49,202 @@ test('listProfiles', async () => { type: 'text/html', }, }, - }, - { - resource: 'profile', - id: 'pfl_znNaTRkJs5', - mode: 'live', - name: 'My website name 2', - website: 'http://www.mywebsite2.com', - email: 'info@mywebsite2.com', - phone: '31123456789', - categoryCode: 5399, - status: 'verified', - review: { - status: 'pending', - }, - createdAt: '2016-01-11T13:03:55+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/profiles/pfl_znNaTRkJs5', - type: 'application/hal+json', - }, - chargebacks: { - href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_znNaTRkJs5', - type: 'application/hal+json', - }, - methods: { - href: 'https://api.mollie.com/v2/methods?profileId=pfl_znNaTRkJs5', - type: 'application/hal+json', + }).twice(); + + const profile = await get(); + + expect(profile.id).toBe('pfl_ahe8z8OPut'); + expect(profile.mode).toBe('live'); + expect(profile.name).toBe('My website name'); + expect(profile.website).toBe('http://www.mywebsite.com'); + expect(profile.email).toBe('info@mywebsite.com'); + expect(profile.phone).toBe('31123456789'); + expect(profile.categoryCode).toBe(5399); + expect(profile.status).toBe('verified'); + expect(profile.review).toEqual({ status: 'pending' }); + + expect(profile._links.self).toEqual({ href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', type: 'application/hal+json' }); + + expect(profile._links.chargebacks).toEqual({ href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); + + expect(profile._links.methods).toEqual({ href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); + + expect(profile._links.payments).toEqual({ href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); + + expect(profile._links.refunds).toEqual({ href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', type: 'application/hal+json' }); + + expect(profile._links.checkoutPreviewUrl).toEqual({ href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', type: 'text/html' }); + }), + ); + }); +}); + +test('listProfiles', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/profiles', 200, { + _embedded: { + profiles: [ + { + resource: 'profile', + id: 'pfl_ahe8z8OPut', + mode: 'live', + name: 'My website name', + website: 'http://www.mywebsite.com', + email: 'info@mywebsite.com', + phone: '31123456789', + categoryCode: 5399, + status: 'verified', + review: { + status: 'pending', }, - payments: { - href: 'https://api.mollie.com/v2/payments?profileId=pfl_znNaTRkJs5', - type: 'application/hal+json', + createdAt: '2016-01-11T13:03:55+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + chargebacks: { + href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + methods: { + href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + payments: { + href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + refunds: { + href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + checkoutPreviewUrl: { + href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', + type: 'text/html', + }, }, - refunds: { - href: 'https://api.mollie.com/v2/refunds?profileId=pfl_znNaTRkJs5', - type: 'application/hal+json', + }, + { + resource: 'profile', + id: 'pfl_znNaTRkJs5', + mode: 'live', + name: 'My website name 2', + website: 'http://www.mywebsite2.com', + email: 'info@mywebsite2.com', + phone: '31123456789', + categoryCode: 5399, + status: 'verified', + review: { + status: 'pending', }, - checkoutPreviewUrl: { - href: 'https://www.mollie.com/payscreen/preview/pfl_znNaTRkJs5', - type: 'text/html', + createdAt: '2016-01-11T13:03:55+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/profiles/pfl_znNaTRkJs5', + type: 'application/hal+json', + }, + chargebacks: { + href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_znNaTRkJs5', + type: 'application/hal+json', + }, + methods: { + href: 'https://api.mollie.com/v2/methods?profileId=pfl_znNaTRkJs5', + type: 'application/hal+json', + }, + payments: { + href: 'https://api.mollie.com/v2/payments?profileId=pfl_znNaTRkJs5', + type: 'application/hal+json', + }, + refunds: { + href: 'https://api.mollie.com/v2/refunds?profileId=pfl_znNaTRkJs5', + type: 'application/hal+json', + }, + checkoutPreviewUrl: { + href: 'https://www.mollie.com/payscreen/preview/pfl_znNaTRkJs5', + type: 'text/html', + }, }, }, - }, - ], - }, - count: 2, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/profiles-api/list-profiles', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.nl/v2/profiles?limit=50', - type: 'application/hal+json', + count: 2, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/profiles-api/list-profiles', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.nl/v2/profiles?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const profiles = await bluster(mollieClient.profiles.page.bind(mollieClient.profiles))(); + const profiles = await bluster(mollieClient.profiles.page.bind(mollieClient.profiles))(); - expect(profiles.length).toBe(2); + expect(profiles.length).toBe(2); - expect(profiles.links.self).toEqual({ href: 'https://api.mollie.nl/v2/profiles?limit=50', type: 'application/hal+json' }); + expect(profiles.links.self).toEqual({ href: 'https://api.mollie.nl/v2/profiles?limit=50', type: 'application/hal+json' }); - expect(profiles.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/profiles-api/list-profiles', type: 'text/html' }); + expect(profiles.links.documentation).toEqual({ href: 'https://docs.mollie.com/reference/v2/profiles-api/list-profiles', type: 'text/html' }); + }); }); -test('updateProfile', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - const expectedWebsiteName = 'Mollie'; - const expectedEmail = 'mollie@mollie.com'; - const expectedPhone = '31123456766'; - - networkMocker.intercept('PATCH', '/profiles/pfl_ahe8z8OPut', 200, { - resource: 'profile', - id: 'pfl_ahe8z8OPut', - mode: 'live', - name: expectedWebsiteName, - website: 'http://www.mywebsite.com', - email: expectedEmail, - phone: expectedPhone, - categoryCode: 5399, - status: 'verified', - review: { - status: 'pending', - }, - createdAt: '2016-01-11T13:03:55+00:00', - _links: { - self: { - href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - chargebacks: { - href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - methods: { - href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', +test('updateProfile', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + const expectedWebsiteName = 'Mollie'; + const expectedEmail = 'mollie@mollie.com'; + const expectedPhone = '31123456766'; + + networkMocker.intercept('PATCH', '/profiles/pfl_ahe8z8OPut', 200, { + resource: 'profile', + id: 'pfl_ahe8z8OPut', + mode: 'live', + name: expectedWebsiteName, + website: 'http://www.mywebsite.com', + email: expectedEmail, + phone: expectedPhone, + categoryCode: 5399, + status: 'verified', + review: { + status: 'pending', }, - payments: { - href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - refunds: { - href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', - type: 'application/hal+json', - }, - checkoutPreviewUrl: { - href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', - type: 'text/html', + createdAt: '2016-01-11T13:03:55+00:00', + _links: { + self: { + href: 'https://api.mollie.com/v2/profiles/pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + chargebacks: { + href: 'https://api.mollie.com/v2/chargebacks?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + methods: { + href: 'https://api.mollie.com/v2/methods?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + payments: { + href: 'https://api.mollie.com/v2/payments?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + refunds: { + href: 'https://api.mollie.com/v2/refunds?profileId=pfl_ahe8z8OPut', + type: 'application/hal+json', + }, + checkoutPreviewUrl: { + href: 'https://www.mollie.com/payscreen/preview/pfl_ahe8z8OPut', + type: 'text/html', + }, }, - }, - }).twice(); + }).twice(); - const profile = await bluster(mollieClient.profiles.update.bind(mollieClient.profiles))('pfl_ahe8z8OPut', { name: expectedWebsiteName, email: expectedEmail, phone: expectedPhone }); + const profile = await bluster(mollieClient.profiles.update.bind(mollieClient.profiles))('pfl_ahe8z8OPut', { name: expectedWebsiteName, email: expectedEmail, phone: expectedPhone }); - expect(profile.name).toBe(expectedWebsiteName); - expect(profile.email).toBe(expectedEmail); - expect(profile.phone).toBe(expectedPhone); + expect(profile.name).toBe(expectedWebsiteName); + expect(profile.email).toBe(expectedEmail); + expect(profile.phone).toBe(expectedPhone); + }); }); diff --git a/tests/unit/resources/refunds.test.ts b/tests/unit/resources/refunds.test.ts index 825a042e..7a960864 100644 --- a/tests/unit/resources/refunds.test.ts +++ b/tests/unit/resources/refunds.test.ts @@ -1,76 +1,75 @@ import { List, Refund } from '../../..'; import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('listRefunds', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/refunds', 200, { - _embedded: { - refunds: [ - { - resource: 'refund', - id: 're_haCsig5aru', - amount: { - value: '2.00', - currency: 'EUR', - }, - status: 'pending', - createdAt: '2018-03-28T10:56:10+00:00', - description: 'My first API payment', - paymentId: 'tr_44aKxzEbr8', - settlementAmount: { - value: '-2.00', - currency: 'EUR', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', - type: 'application/hal+json', +test('listRefunds', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/refunds', 200, { + _embedded: { + refunds: [ + { + resource: 'refund', + id: 're_haCsig5aru', + amount: { + value: '2.00', + currency: 'EUR', + }, + status: 'pending', + createdAt: '2018-03-28T10:56:10+00:00', + description: 'My first API payment', + paymentId: 'tr_44aKxzEbr8', + settlementAmount: { + value: '-2.00', + currency: 'EUR', }, - payment: { - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', + type: 'application/hal+json', + }, + payment: { + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/refunds-api/list-refunds', - type: 'text/html', + ], }, - self: { - href: 'http://api.mollie.nl/v2/refunds?limit=10', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/refunds-api/list-refunds', + type: 'text/html', + }, + self: { + href: 'http://api.mollie.nl/v2/refunds?limit=10', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - count: 1, - }).twice(); + count: 1, + }).twice(); - const refunds: List = await bluster(mollieClient.refunds.page.bind(mollieClient.refunds))(); + const refunds: List = await bluster(mollieClient.refunds.page.bind(mollieClient.refunds))(); - expect(refunds.length).toBe(1); + expect(refunds.length).toBe(1); - const refund = refunds[0]; - expect(refund.id).toBe('re_haCsig5aru'); - expect(refund.amount).toEqual({ value: '2.00', currency: 'EUR' }); - expect(refund.status).toBe('pending'); - expect(refund.createdAt).toBe('2018-03-28T10:56:10+00:00'); - expect(refund.description).toBe('My first API payment'); - expect(refund.paymentId).toBe('tr_44aKxzEbr8'); - expect(refund.settlementAmount).toEqual({ value: '-2.00', currency: 'EUR' }); + const refund = refunds[0]; + expect(refund.id).toBe('re_haCsig5aru'); + expect(refund.amount).toEqual({ value: '2.00', currency: 'EUR' }); + expect(refund.status).toBe('pending'); + expect(refund.createdAt).toBe('2018-03-28T10:56:10+00:00'); + expect(refund.description).toBe('My first API payment'); + expect(refund.paymentId).toBe('tr_44aKxzEbr8'); + expect(refund.settlementAmount).toEqual({ value: '-2.00', currency: 'EUR' }); - expect(refund._links.self).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', - type: 'application/hal+json', - }); + expect(refund._links.self).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8/refunds/re_haCsig5aru', + type: 'application/hal+json', + }); - expect(refund._links.payment).toEqual({ - href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', - type: 'application/hal+json', + expect(refund._links.payment).toEqual({ + href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', + type: 'application/hal+json', + }); }); }); diff --git a/tests/unit/resources/subscriptions.test.ts b/tests/unit/resources/subscriptions.test.ts index e6c9218a..7df14f09 100644 --- a/tests/unit/resources/subscriptions.test.ts +++ b/tests/unit/resources/subscriptions.test.ts @@ -1,61 +1,60 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -test('listPageOfRootSubscriptions', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/subscriptions', 200, { - _embedded: { - subscriptions: [ - { - resource: 'subscription', - id: 'sub_wByQa6efm6', - mode: 'test', - createdAt: '2018-04-24T11:41:55+00:00', - status: 'active', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Order 1234', - method: null, - times: null, - interval: '1 month', - startDate: '2018-04-24', - webhookUrl: null, - _links: { - self: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', - type: 'application/hal+json', +test('listPageOfRootSubscriptions', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/subscriptions', 200, { + _embedded: { + subscriptions: [ + { + resource: 'subscription', + id: 'sub_wByQa6efm6', + mode: 'test', + createdAt: '2018-04-24T11:41:55+00:00', + status: 'active', + amount: { + value: '10.00', + currency: 'EUR', }, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', - type: 'application/hal+json', + description: 'Order 1234', + method: null, + times: null, + interval: '1 month', + startDate: '2018-04-24', + webhookUrl: null, + _links: { + self: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n/subscriptions/sub_wByQa6efm6', + type: 'application/hal+json', + }, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_FhQJRw4s2n', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - count: 1, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/subscriptions?limit=50', - type: 'application/hal+json', + count: 1, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/subscriptions?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - }).twice(); + }).twice(); - const subscriptions = await bluster(mollieClient.subscription.page.bind(mollieClient.subscription))(); + const subscriptions = await bluster(mollieClient.subscription.page.bind(mollieClient.subscription))(); - expect(subscriptions.length).toBe(1); + expect(subscriptions.length).toBe(1); - expect(subscriptions[0].resource).toBe('subscription'); - expect(subscriptions[0].id).toBe('sub_wByQa6efm6'); - // No need to test all attributes here ... + expect(subscriptions[0].resource).toBe('subscription'); + expect(subscriptions[0].id).toBe('sub_wByQa6efm6'); + // No need to test all attributes here ... + }); }); diff --git a/tests/unit/resources/subscriptions/payments.test.ts b/tests/unit/resources/subscriptions/payments.test.ts index 7420117d..b50f0cc7 100644 --- a/tests/unit/resources/subscriptions/payments.test.ts +++ b/tests/unit/resources/subscriptions/payments.test.ts @@ -1,154 +1,153 @@ import NetworkMocker, { getApiKeyClientProvider } from '../../../NetworkMocker'; -test('listSubscriptionPayments', async () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider()); - const mollieClient = await networkMocker.prepare(); - - networkMocker.intercept('GET', '/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments', 200, { - _embedded: { - payments: [ - { - resource: 'payment', - id: 'tr_DtKxVP2AgW', - mode: 'test', - createdAt: '2018-09-19T12:49:52+00:00', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Payment no 1', - method: 'directdebit', - metadata: null, - status: 'pending', - isCancelable: true, - expiresAt: '2019-09-19T12:49:52+00:00', - locale: 'nl_NL', - profileId: 'pfl_rH9rQtedgS', - customerId: 'cst_8wmqcHMN4U', - mandateId: 'mdt_aGQNkteF6w', - subscriptionId: 'sub_8JfGzs6v3K', - sequenceType: 'recurring', - redirectUrl: null, - cancelUrl: null, - webhookUrl: 'https://example.org/webhook', - settlementAmount: { - value: '10.00', - currency: 'EUR', - }, - details: { - transferReference: 'SD67-6850-2204-6029', - creditorIdentifier: 'NL08ZZZ502057730000', - consumerName: 'Customer A', - consumerAccount: 'NL50INGB0006588912', - consumerBic: 'INGBNL2A', - dueDate: '2018-09-21', - signatureDate: '2018-09-19', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_DtKxVP2AgW', - type: 'application/hal+json', +test('listSubscriptionPayments', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker.intercept('GET', '/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments', 200, { + _embedded: { + payments: [ + { + resource: 'payment', + id: 'tr_DtKxVP2AgW', + mode: 'test', + createdAt: '2018-09-19T12:49:52+00:00', + amount: { + value: '10.00', + currency: 'EUR', }, - checkout: null, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U', - type: 'application/hal+json', + description: 'Payment no 1', + method: 'directdebit', + metadata: null, + status: 'pending', + isCancelable: true, + expiresAt: '2019-09-19T12:49:52+00:00', + locale: 'nl_NL', + profileId: 'pfl_rH9rQtedgS', + customerId: 'cst_8wmqcHMN4U', + mandateId: 'mdt_aGQNkteF6w', + subscriptionId: 'sub_8JfGzs6v3K', + sequenceType: 'recurring', + redirectUrl: null, + cancelUrl: null, + webhookUrl: 'https://example.org/webhook', + settlementAmount: { + value: '10.00', + currency: 'EUR', }, - mandate: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_aGQNkteF6w', - type: 'application/hal+json', + details: { + transferReference: 'SD67-6850-2204-6029', + creditorIdentifier: 'NL08ZZZ502057730000', + consumerName: 'Customer A', + consumerAccount: 'NL50INGB0006588912', + consumerBic: 'INGBNL2A', + dueDate: '2018-09-21', + signatureDate: '2018-09-19', }, - subscription: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K', - type: 'application/hal+json', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_DtKxVP2AgW', + type: 'application/hal+json', + }, + checkout: null, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U', + type: 'application/hal+json', + }, + mandate: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_aGQNkteF6w', + type: 'application/hal+json', + }, + subscription: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K', + type: 'application/hal+json', + }, }, }, - }, - { - resource: 'payment', - id: 'tr_DtKxVP2AgD', - mode: 'test', - createdAt: '2018-09-19T12:49:52+00:00', - amount: { - value: '10.00', - currency: 'EUR', - }, - description: 'Payment no 2', - method: 'directdebit', - metadata: null, - status: 'paid', - isCancelable: true, - expiresAt: '2019-09-19T12:49:52+00:00', - locale: 'nl_NL', - profileId: 'pfl_rH9rQtedgS', - customerId: 'cst_8wmqcHMN4U', - mandateId: 'mdt_aGQNkteF6w', - subscriptionId: 'sub_8JfGzs6v3K', - sequenceType: 'recurring', - redirectUrl: null, - cancelUrl: null, - webhookUrl: 'https://example.org/webhook', - settlementAmount: { - value: '10.00', - currency: 'EUR', - }, - details: { - transferReference: 'SD67-6850-2204-6029', - creditorIdentifier: 'NL08ZZZ502057730000', - consumerName: 'Customer A', - consumerAccount: 'NL50INGB0006588912', - consumerBic: 'INGBNL2A', - dueDate: '2018-09-21', - signatureDate: '2018-09-19', - }, - _links: { - self: { - href: 'https://api.mollie.com/v2/payments/tr_DtKxVP2AgD', - type: 'application/hal+json', + { + resource: 'payment', + id: 'tr_DtKxVP2AgD', + mode: 'test', + createdAt: '2018-09-19T12:49:52+00:00', + amount: { + value: '10.00', + currency: 'EUR', }, - checkout: null, - customer: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U', - type: 'application/hal+json', + description: 'Payment no 2', + method: 'directdebit', + metadata: null, + status: 'paid', + isCancelable: true, + expiresAt: '2019-09-19T12:49:52+00:00', + locale: 'nl_NL', + profileId: 'pfl_rH9rQtedgS', + customerId: 'cst_8wmqcHMN4U', + mandateId: 'mdt_aGQNkteF6w', + subscriptionId: 'sub_8JfGzs6v3K', + sequenceType: 'recurring', + redirectUrl: null, + cancelUrl: null, + webhookUrl: 'https://example.org/webhook', + settlementAmount: { + value: '10.00', + currency: 'EUR', }, - mandate: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_aGQNkteF6w', - type: 'application/hal+json', + details: { + transferReference: 'SD67-6850-2204-6029', + creditorIdentifier: 'NL08ZZZ502057730000', + consumerName: 'Customer A', + consumerAccount: 'NL50INGB0006588912', + consumerBic: 'INGBNL2A', + dueDate: '2018-09-21', + signatureDate: '2018-09-19', }, - subscription: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K', - type: 'application/hal+json', + _links: { + self: { + href: 'https://api.mollie.com/v2/payments/tr_DtKxVP2AgD', + type: 'application/hal+json', + }, + checkout: null, + customer: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U', + type: 'application/hal+json', + }, + mandate: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_aGQNkteF6w', + type: 'application/hal+json', + }, + subscription: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K', + type: 'application/hal+json', + }, }, }, - }, - ], - }, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions-payments', - type: 'text/html', + ], }, - self: { - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments?limit=50', - type: 'application/hal+json', + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions-payments', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, }, - previous: null, - next: null, - }, - count: 2, - }).twice(); + count: 2, + }).twice(); - const payments = await bluster(mollieClient.subscriptionPayments.page.bind(mollieClient.subscriptionPayments))({ customerId: 'cst_8wmqcHMN4U', subscriptionId: 'sub_8JfGzs6v3K' }); + const payments = await bluster(mollieClient.subscriptionPayments.page.bind(mollieClient.subscriptionPayments))({ customerId: 'cst_8wmqcHMN4U', subscriptionId: 'sub_8JfGzs6v3K' }); - expect(payments.length).toBe(2); + expect(payments.length).toBe(2); - expect(payments.links.documentation).toEqual({ - href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions-payments', - type: 'text/html', - }); + expect(payments.links.documentation).toEqual({ + href: 'https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions-payments', + type: 'text/html', + }); - expect(payments.links.self).toEqual({ - href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments?limit=50', - type: 'application/hal+json', + expect(payments.links.self).toEqual({ + href: 'https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/subscriptions/sub_8JfGzs6v3K/payments?limit=50', + type: 'application/hal+json', + }); }); }); From 9e97c1b45922e1f230e5960038ec3aa6d6cbc506 Mon Sep 17 00:00:00 2001 From: Demian Sempel Date: Thu, 1 Aug 2024 17:08:40 +0200 Subject: [PATCH 05/23] Fix retrying mechanism for fetch --- src/communication/NetworkClient.ts | 31 +++++----- src/communication/makeRetrying.ts | 94 +++++++++++------------------- src/errors/ApiError.ts | 7 +-- 3 files changed, 54 insertions(+), 78 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 4603ec57..d2390d7e 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -1,11 +1,12 @@ import https from 'https'; +import fetch, { type RequestInit } from 'node-fetch'; import { type SecureContextOptions } from 'tls'; -import fetch, { type RequestInit, type Response } from 'node-fetch'; import { run } from 'ruply'; import type Page from '../data/page/Page'; import ApiError from '../errors/ApiError'; import type Options from '../Options'; +import fling from '../plumbing/fling'; import DemandingIterator from '../plumbing/iteration/DemandingIterator'; import HelpfulIterator from '../plumbing/iteration/HelpfulIterator'; import Throttler from '../plumbing/Throttler'; @@ -14,8 +15,7 @@ import { type IdempotencyParameter } from '../types/parameters'; import breakUrl from './breakUrl'; import buildUrl, { type SearchParameters } from './buildUrl'; import dromedaryCase from './dromedaryCase'; -import { idempotencyHeaderName } from './makeRetrying'; -import fling from '../plumbing/fling'; +import { idempotencyHeaderName, ResponseWithIdempotencyKey, retryingFetch } from './makeRetrying'; /** * Like `[].map` but with support for non-array inputs, in which case this function behaves as if an array was passed @@ -75,14 +75,14 @@ const throwApiError = run( /** * Checks if an API error needs to be thrown based on the passed result. */ -async function processFetchResponse(res: Response) { +async function processFetchResponse(res: ResponseWithIdempotencyKey) { // Request was successful, but no content was returned. if (res.status == 204) return true; // Request was successful and content was returned. const body = await res.json(); if (res.status >= 200 && res.status < 300) return body; // Request was not successful, but the response body contains an error message. - if (body) throw ApiError.createFromResponse(body, res.headers); + if (body) throw ApiError.createFromResponse(body, res.idempotencyKey); // Request was not successful. throw new ApiError('An unknown error has occurred'); } @@ -94,7 +94,7 @@ interface Context {} * This class is essentially a wrapper around axios. It simplifies communication with the Mollie API over the network. */ export default class NetworkClient { - protected readonly request: (pathname: string, options?: RequestInit) => Promise; + protected readonly request: (pathname: string, options?: RequestInit) => Promise; constructor({ apiKey, accessToken, @@ -120,11 +120,14 @@ export default class NetworkClient { // Create the https agent. const agent = new https.Agent({ ca: caCertificates }); + // Create retrying fetch function. + const fetchWithRetries = retryingFetch(fetch); + // Create the request function. this.request = (pathname, options) => { // If the pathname starts with a slash, remove it and prepend the API endpoint. const url = pathname.startsWith('/') ? `${apiEndpoint}${pathname.substring(1)}` : pathname; - return fetch(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); + return fetchWithRetries(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); }; // Make the Axios instance request multiple times in some scenarios. @@ -142,15 +145,15 @@ export default class NetworkClient { headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, body: JSON.stringify(body), }; - return this.request(buildUrl(pathname, query), config).then(processFetchResponse).catch(throwApiError); + return this.request(buildUrl(pathname, query), config).catch(throwApiError).then(processFetchResponse); } async get(pathname: string, query?: SearchParameters): Promise { - return this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); + return this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); } async list(pathname: string, binderName: string, query?: SearchParameters): Promise { - const data = await this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); + const data = await this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded } = data; @@ -161,7 +164,7 @@ export default class NetworkClient { } async page(pathname: string, binderName: string, query?: SearchParameters): Promise, 'links' | 'count'>> { - const data = await this.request(buildUrl(pathname, query)).then(processFetchResponse).catch(throwApiError); + const data = await this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded, _links: links, count } = data; @@ -210,7 +213,7 @@ export default class NetworkClient { let url = buildUrl(pathname, { ...query, limit: popLimit() }); while (true) { // Request and parse the page from the Mollie API. - const data = await request(url).then(processFetchResponse).catch(throwApiError); + const data = await request(url).catch(throwApiError).then(processFetchResponse); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded, _links: links } = data; @@ -242,7 +245,7 @@ export default class NetworkClient { method: 'PATCH', body: JSON.stringify(data), }; - return this.request(buildUrl(pathname), config).then(processFetchResponse).catch(throwApiError); + return this.request(buildUrl(pathname), config).catch(throwApiError).then(processFetchResponse); } async delete(pathname: string, context?: Context & IdempotencyParameter): Promise { @@ -253,6 +256,6 @@ export default class NetworkClient { headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined, }; - return this.request(buildUrl(pathname), config).then(processFetchResponse).catch(throwApiError); + return this.request(buildUrl(pathname), config).catch(throwApiError).then(processFetchResponse); } } diff --git a/src/communication/makeRetrying.ts b/src/communication/makeRetrying.ts index 08f00c15..1920d224 100644 --- a/src/communication/makeRetrying.ts +++ b/src/communication/makeRetrying.ts @@ -1,14 +1,6 @@ import { randomBytes } from 'crypto'; -import axios, { type InternalAxiosRequestConfig, type AxiosRequestConfig, type AxiosError, type AxiosInstance, type AxiosResponse, type Method } from 'axios'; - -/** - * The name of the property in the request configuration which indicates which attempt this is. `0` for the initial - * attempt, `1` for the first retry (second attempt), et cetera. - * - * This must not overlap with any of the other properties, and for technical reasons, cannot be a symbol. - */ -const attemptIndex = '_attemptIndex'; +import fetch, { type RequestInit, type Response } from 'node-fetch'; /** * The delay, in milliseconds, between attempts. Note that if Axios' timeout is smaller than this value, no second @@ -22,9 +14,7 @@ const attemptLimit = 3; /** * A set of the methods which are not idempotent by nature, specifically `POST` and `DELETE`. */ -const unsafeMethods = new Set(['delete', 'DELETE', 'post', 'POST'] satisfies Array); - -type AttemptState = { [attemptIndex]: number }; +const unsafeMethods = new Set(['DELETE', 'POST']); /** * The name of the header generated by `generateIdempotencyHeader`. @@ -42,16 +32,6 @@ function generateIdempotencyHeader() { return { [idempotencyHeaderName]: randomBytes(18).toString('base64') }; } -/** - * Returns whether the passed error is as expected: an Axios error whose config has an attemptIndex property. - */ -function checkError(error: any): error is AxiosError & { config: AxiosRequestConfig & AttemptState } { - return ( - axios.isAxiosError(error) && // Note that Axios errors always have a config property. - (error.config as AxiosRequestConfig & Partial)[attemptIndex] != undefined - ); -} - /** * Parses the 'Retry-After' header, if any, and returns the time in milliseconds. Returns `undefined` if the header is * not present or not parseable into a numeric value. @@ -59,11 +39,11 @@ function checkError(error: any): error is AxiosError & { config: AxiosRequestCon * If the header contains an HTTP date rather than a delay, it will be ignored. * @see https://httpwg.org/specs/rfc9110.html#field.retry-after */ -function parseRetryAfterHeader(response: AxiosResponse): number | undefined { +function parseRetryAfterHeader(response: fetch.Response): number | undefined { // (If the header does not exist, the input of parseInt will be undefined, resulting in NaN. The non-null assertion // is thus safe.) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const retryAfter = parseInt(response.headers['retry-after']!, 10); + const retryAfter = parseInt(response.headers.get('retry-after')!, 10); if (isNaN(retryAfter)) { return undefined; } @@ -71,8 +51,10 @@ function parseRetryAfterHeader(response: AxiosResponse): number | undefined { return retryAfter * 1e3; } +export type ResponseWithIdempotencyKey = Response & { idempotencyKey: string | undefined }; + /** - * Enhances the passed Axios instance, making it attempt requests multiple times in some scenarios. + * Wrapper around fetch, making it attempt requests multiple times in some scenarios. * * The idea is that if the Mollie API has a brief hiccup, the extra attempts may cause the request to succeed anyway. * @@ -85,40 +67,34 @@ function parseRetryAfterHeader(response: AxiosResponse): number | undefined { * request being re-attempted from two separate similarly-looking requests. In effect, this allows this client to * safely re-attempt requests. */ -export default function makeRetrying(axiosInstance: AxiosInstance) { - // Intercept all outgoing requests. - axiosInstance.interceptors.request.use((config: AxiosRequestConfig & Partial) => { - // If the request is a POST or DELETE one and does not yet have the idempotency header, add one now. (If no method - // is set, it defaults to GET. Since neither GET nor undefined exist in the unsafeMethods set, the non-null - // assertion is safe.) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (unsafeMethods.has(config.method!) && config.headers?.[idempotencyHeaderName] == undefined) { - Object.assign((config.headers ??= {}), generateIdempotencyHeader()); - } - // Set the attempt (in the request configuration). - config[attemptIndex] = (config[attemptIndex] ?? -1) + 1; - return config as InternalAxiosRequestConfig; - }); - // Intercept any erroneous responses, and consider doing another attempt. - axiosInstance.interceptors.response.use(undefined, error => { - // If the request configuration is unexpected ‒ in other words, it seems the request did not go through the - // interceptor above ‒ do not make another attempt. - if (!checkError(error)) { - return Promise.reject(error); +export function retryingFetch(originalFetch: typeof fetch) { + return async function fetchWithRetries(input: string, init: RequestInit): Promise { + // If the request is a POST or DELETE one and does not yet have the idempotency header, add one now. + if (init.method != undefined && unsafeMethods.has(init.method.toUpperCase()) && init.headers && !(idempotencyHeaderName in init.headers)) { + init.headers = { ...init.headers, ...generateIdempotencyHeader() }; } - const { config } = error; - // If the attempt limit has been reached, do not make another attempt. - if (config[attemptIndex] == attemptLimit - 1) { - return Promise.reject(error); + // Attempt the request. + for (let attempt = 0; ; ++attempt) { + const response = await originalFetch(input, init).then(response => + Object.assign(response, { idempotencyKey: init.headers && idempotencyHeaderName in init.headers ? (init.headers[idempotencyHeaderName] as string) : undefined }), + ); + // If this is the last attempt, return the response. + if (attempt === attemptLimit - 1) { + return response; + } + // If the response is not a 5×× status code, return it. + if (Math.floor(response.status / 100) != 5) { + return response; + } + // Determine the delay after which the next attempt is made, preferring a Retry-After header defined in the + // response over the default value. + const delay = parseRetryAfterHeader(response) ?? retryDelay; + // Wait before the next attempt. + await sleep(delay); } - // If there is no response or the HTTP status code of the response is not 5××, do not make another attempt. - if (error.response == undefined || Math.floor(error.response.status / 100) != 5) { - return Promise.reject(error); - } - // Determine the delay after which the next attempt is made, preferring a Retry-After header defined in the - // response over the default value. - const delay = parseRetryAfterHeader(error.response) ?? retryDelay; - // Schedule the attempt. - return new Promise(resolve => setTimeout(() => resolve(axiosInstance.request(config)), delay)); - }); + }; +} + +function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts index 6a16a36c..a6bc595b 100644 --- a/src/errors/ApiError.ts +++ b/src/errors/ApiError.ts @@ -1,6 +1,3 @@ -import { type Headers } from 'node-fetch'; - -import { idempotencyHeaderName } from '../communication/makeRetrying'; import { type Links, type Url } from '../data/global'; import type Maybe from '../types/Maybe'; @@ -112,8 +109,8 @@ export default class ApiError extends Error { * * @since 3.0.0 */ - public static createFromResponse(body: any, headers: Headers): ApiError { + public static createFromResponse(body: any, idempotencyKey: string | undefined): ApiError { const { detail, title, status: statusCode, field, _links: links } = body; - return new ApiError(detail ?? 'Received an error without a message', { title, statusCode, field, links, idempotencyKey: headers.get(idempotencyHeaderName) ?? undefined }); + return new ApiError(detail ?? 'Received an error without a message', { title, statusCode, field, links, idempotencyKey }); } } From f07a902936bbe5bac81260e5329f3172b62d2831 Mon Sep 17 00:00:00 2001 From: Demian Sempel Date: Thu, 1 Aug 2024 21:06:41 +0200 Subject: [PATCH 06/23] Fix some more tests and remove axios references --- src/communication/NetworkClient.ts | 5 +- src/communication/makeRetrying.ts | 5 +- tests/communication/request-retrying.test.ts | 3 - tests/integration/chargebacks.test.ts | 6 - tests/integration/customers.test.ts | 6 - tests/integration/methods.test.ts | 6 - tests/integration/orders.test.ts | 6 - tests/integration/payments.test.ts | 6 - tests/integration/refunds.test.ts | 6 - .../__snapshots__/lists.test.ts.snap | 65 ----------- tests/unit/resources/lists.test.ts | 103 +++++------------- tests/unit/resources/methods.test.ts | 88 ++++++++------- 12 files changed, 82 insertions(+), 223 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index d2390d7e..261e4a01 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -91,7 +91,7 @@ interface Data {} interface Context {} /** - * This class is essentially a wrapper around axios. It simplifies communication with the Mollie API over the network. + * This class is essentially a wrapper around fetch. It simplifies communication with the Mollie API over the network. */ export default class NetworkClient { protected readonly request: (pathname: string, options?: RequestInit) => Promise; @@ -129,9 +129,6 @@ export default class NetworkClient { const url = pathname.startsWith('/') ? `${apiEndpoint}${pathname.substring(1)}` : pathname; return fetchWithRetries(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); }; - - // Make the Axios instance request multiple times in some scenarios. - // makeRetrying(this.axiosInstance); } async post(pathname: string, data: Data & IdempotencyParameter, query?: SearchParameters): Promise { diff --git a/src/communication/makeRetrying.ts b/src/communication/makeRetrying.ts index 1920d224..91a7fb1e 100644 --- a/src/communication/makeRetrying.ts +++ b/src/communication/makeRetrying.ts @@ -3,8 +3,7 @@ import { randomBytes } from 'crypto'; import fetch, { type RequestInit, type Response } from 'node-fetch'; /** - * The delay, in milliseconds, between attempts. Note that if Axios' timeout is smaller than this value, no second - * attempt will ever be made. + * The delay, in milliseconds, between attempts. */ const retryDelay = 2e3; /** @@ -61,7 +60,7 @@ export type ResponseWithIdempotencyKey = Response & { idempotencyKey: string | u * If the Mollie API responds with a 5×× status code, the request will be re-attempted until: * * the Mollie API responds with a different status code, or * * the attempt limit has been reached (it gives up after the third attempt), or - * * the request has timed out (as per the timeout set in the Axios instance). + * * the request has timed out. * * For `POST` and `DELETE` requests, an idempotency key is added. This ensures the Mollie API can distinguish a single * request being re-attempted from two separate similarly-looking requests. In effect, this allows this client to diff --git a/tests/communication/request-retrying.test.ts b/tests/communication/request-retrying.test.ts index 7870cbdc..8ed4f797 100644 --- a/tests/communication/request-retrying.test.ts +++ b/tests/communication/request-retrying.test.ts @@ -269,8 +269,5 @@ describe('request-retrying', () => { jest.useRealTimers(); }); - // Potentially worth testing: - // * Is Axios' timeout still respected? - afterAll(() => networkMocker.cleanup()); }); diff --git a/tests/integration/chargebacks.test.ts b/tests/integration/chargebacks.test.ts index 8f3a549a..91ad3878 100644 --- a/tests/integration/chargebacks.test.ts +++ b/tests/integration/chargebacks.test.ts @@ -1,12 +1,6 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import createMollieClient from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/integration/customers.test.ts b/tests/integration/customers.test.ts index e63a699d..8e0d7ccc 100644 --- a/tests/integration/customers.test.ts +++ b/tests/integration/customers.test.ts @@ -1,12 +1,6 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import createMollieClient from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/integration/methods.test.ts b/tests/integration/methods.test.ts index 9532c2df..1e7df945 100644 --- a/tests/integration/methods.test.ts +++ b/tests/integration/methods.test.ts @@ -1,12 +1,6 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import createMollieClient from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/integration/orders.test.ts b/tests/integration/orders.test.ts index d1c7bb22..71131944 100644 --- a/tests/integration/orders.test.ts +++ b/tests/integration/orders.test.ts @@ -1,14 +1,8 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import { fail } from 'node:assert'; import createMollieClient, { Locale, OrderEmbed, OrderLineType, Payment, PaymentMethod } from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/integration/payments.test.ts b/tests/integration/payments.test.ts index 0680f7ca..3de4516e 100644 --- a/tests/integration/payments.test.ts +++ b/tests/integration/payments.test.ts @@ -1,14 +1,8 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import { fail } from 'node:assert'; import createMollieClient from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/integration/refunds.test.ts b/tests/integration/refunds.test.ts index 078a2e12..e6e8639f 100644 --- a/tests/integration/refunds.test.ts +++ b/tests/integration/refunds.test.ts @@ -1,12 +1,6 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import createMollieClient from '../..'; -/** - * Overwrite the default XMLHttpRequestAdapter - */ -axios.defaults.adapter = 'http'; - /** * Load the API_KEY environment variable */ diff --git a/tests/unit/resources/__snapshots__/lists.test.ts.snap b/tests/unit/resources/__snapshots__/lists.test.ts.snap index 939d9737..2d43b8a4 100644 --- a/tests/unit/resources/__snapshots__/lists.test.ts.snap +++ b/tests/unit/resources/__snapshots__/lists.test.ts.snap @@ -64,68 +64,3 @@ exports[`lists .list() should retrieve the next page 1`] = ` }, ] `; - -exports[`lists .list() should retrieve the next page 2`] = ` -[ - { - "_links": { - "documentation": { - "href": "https://docs.mollie.com/reference/v2/customers-api/get-customer", - "type": "text/html", - }, - "self": { - "href": "https://api.mollie.com/v2/customers/cst_l4J9zsdzO", - "type": "application/hal+json", - }, - }, - "createdAt": "2018-04-06T13:23:21.0Z", - "email": "customer4@example.org", - "id": "cst_l4J9zsdzO", - "locale": "nl_NL", - "metadata": null, - "mode": "test", - "name": "Customer 4", - "resource": "customer", - }, - { - "_links": { - "documentation": { - "href": "https://docs.mollie.com/reference/v2/customers-api/get-customer", - "type": "text/html", - }, - "self": { - "href": "https://api.mollie.com/v2/customers/cst_CFAhWkIKz", - "type": "application/hal+json", - }, - }, - "createdAt": "2018-04-06T13:23:21.0Z", - "email": "customer5@example.org", - "id": "cst_CFAhWkIKz", - "locale": "nl_NL", - "metadata": null, - "mode": "test", - "name": "Customer 5", - "resource": "customer", - }, - { - "_links": { - "documentation": { - "href": "https://docs.mollie.com/reference/v2/customers-api/get-customer", - "type": "text/html", - }, - "self": { - "href": "https://api.mollie.com/v2/customers/cst_5BsP6Y1S8", - "type": "application/hal+json", - }, - }, - "createdAt": "2018-04-06T13:23:21.0Z", - "email": "customer6@example.org", - "id": "cst_5BsP6Y1S8", - "locale": "nl_NL", - "metadata": null, - "mode": "test", - "name": "Customer 6", - "resource": "customer", - }, -] -`; diff --git a/tests/unit/resources/lists.test.ts b/tests/unit/resources/lists.test.ts index 5207add5..258806a1 100644 --- a/tests/unit/resources/lists.test.ts +++ b/tests/unit/resources/lists.test.ts @@ -1,15 +1,9 @@ -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - import CustomersBinder from '../../../src/binders/customers/CustomersBinder'; import NetworkClient from '../../../src/communication/NetworkClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; import page1 from '../__stubs__/list/customers_page_1.json'; import page2 from '../__stubs__/list/customers_page_2.json'; -import page3 from '../__stubs__/list/customers_page_3.json'; -import Page from '../../../src/data/page/Page'; - -const mock = new MockAdapter(axios); describe('lists', () => { let customers: CustomersBinder; @@ -18,78 +12,39 @@ describe('lists', () => { }); describe('.list()', () => { - mock.onGet('/customers?limit=3').reply(200, page1); - mock.onGet('/customers?limit=3&from=cst_kEn1PlbGa').reply(200, page1); - mock.onGet('/customers?limit=3&from=cst_l4J9zsdzO').reply(200, page2); - mock.onGet('/customers?limit=3&from=cst_1DVwgVBLS').reply(200, page3); - - it('should retrieve a limited list', done => { - customers - .page({ limit: 3 }) - .then(result => { - expect(result[2].resource).toEqual('customer'); - expect(result[3]).toBeUndefined(); - done(); - }) - .catch(err => { - expect(err).toBeUndefined(); - done(); - }); - }); - - it('should retrieve the next page', done => { - customers - .page({ limit: 3 }) - .then(result => { - result.nextPage().then(list => { - expect(list[0].id).toEqual('cst_l4J9zsdzO'); - expect(list).toMatchSnapshot(); - done(); - }); - }) - .catch(err => { - expect(err).toBeUndefined(); - done(); - }); + it('should retrieve a limited list', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/customers?limit=3', 200, page1); + const result = await customers.page({ limit: 3 }); + expect(result).toHaveLength(3); + expect(result[2].resource).toEqual('customer'); + }); }); - it('should retrieve the next page', done => { - customers - .page({ limit: 3 }) - .then(result => { - result - .nextPage() - .then((list: Page) => { - expect(list[0].id).toEqual('cst_l4J9zsdzO'); - expect(list.nextPageCursor).toEqual('cst_1DVwgVBLS'); - expect(list).toMatchSnapshot(); - done(); - }) - .catch(err => { - expect(err).toBeUndefined(); - }); - }) - .catch(err => { - expect(err).toBeUndefined(); - }); + it('should retrieve the next page', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/customers?limit=3', 200, page1); + networkMocker.intercept('GET', '/customers?limit=3&from=cst_l4J9zsdzO', 200, page2); + const list1 = await customers.page({ limit: 3 }); + const list2 = await list1.nextPage?.(); + expect(list2).toHaveLength(3); + expect(list2![0].id).toEqual('cst_l4J9zsdzO'); + expect(list2?.nextPageCursor).toEqual('cst_1DVwgVBLS'); + expect(list2).toMatchSnapshot(); + }); }); - xit('should retrieve all pages with a callback', done => { - let i = 0; - const expected = ['cst_kEn1PlbGa', 'cst_l4J9zsdzO', 'cst_1DVwgVBLS', undefined]; - - const handleNextPage = (err, result: Page): void => { - expect(err).toBeNull(); - expect(result[0].id).toEqual(expected[i]); - expect(result.nextPageCursor).toEqual(expected[++i]); - if (i === 3) { + it('should retrieve page with a callback', done => { + new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/customers?limit=3', 200, page1); + customers.page({ limit: 3 }, (err, result): void => { + expect(err).toBeNull(); + expect(result[0].id).toEqual('cst_kEn1PlbGa'); + expect(result.nextPageCursor).toEqual('cst_l4J9zsdzO'); + expect(result.nextPage).toBeDefined(); done(); - } else { - result.nextPage().then(); - } - }; - - customers.page({ limit: 3 }, handleNextPage).then(); + }); + }); }); }); }); diff --git a/tests/unit/resources/methods.test.ts b/tests/unit/resources/methods.test.ts index 1d2c1502..f41f38d4 100644 --- a/tests/unit/resources/methods.test.ts +++ b/tests/unit/resources/methods.test.ts @@ -1,13 +1,9 @@ -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - import MethodsBinder from '../../../src/binders/methods/MethodsBinder'; import NetworkClient from '../../../src/communication/NetworkClient'; +import NetworkMocker, { getApiKeyClientProvider } from '../../NetworkMocker'; -import response from '../__stubs__/methods.json'; import ApiError from '../../../src/errors/ApiError'; - -const mock = new MockAdapter(axios); +import response from '../__stubs__/methods.json'; describe('methods', () => { let methods: MethodsBinder; @@ -19,54 +15,70 @@ describe('methods', () => { const methodId = 'ideal'; const error = { detail: 'The method id is invalid' }; - mock.onGet(`/methods/${methodId}`).reply(200, response._embedded.methods[0], {}); - mock.onGet('/methods/foo').reply(500, error, {}); - - it('should return a method instance', () => - methods.get(methodId).then(result => { + it('should return a method instance', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', `/methods/${methodId}`, 200, response._embedded.methods[0]); + const result = await methods.get(methodId); expect(result).toMatchSnapshot(); - })); + }); + }); it('should work with a callback', done => { - methods.get(methodId, {}, (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchSnapshot(); - done(); + new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', `/methods/${methodId}`, 200, response._embedded.methods[0]); + methods.get(methodId, {}, (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchSnapshot(); + done(); + }); }); }); - it('should throw an error for non-existent IDs', () => - methods - .get('foo') - .then(result => expect(result).toBeUndefined()) - .catch(err => { - expect(err).toBeInstanceOf(ApiError); - expect(err.getMessage()).toEqual(error.detail); - })); + it('should throw an error for non-existent IDs', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/methods/foo', 500, error); + await methods + .get('foo') + .then(() => { + throw new Error('Promise should throw'); + }) + .catch(err => { + expect(err).toBeInstanceOf(ApiError); + expect(err.getMessage()).toEqual(error.detail); + }); + }); + }); it('should return an error with a callback for non-existent IDs', done => { - methods.get('foo', {}, (err: any, result) => { - expect(err).toBeInstanceOf(ApiError); - expect(err.getMessage()).toEqual(error.detail); - expect(result).toBeUndefined(); - done(); + new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/methods/foo', 500, error); + methods.get('foo', {}, (err: any, result) => { + expect(err).toBeInstanceOf(ApiError); + expect(err.getMessage()).toEqual(error.detail); + expect(result).toBeUndefined(); + done(); + }); }); }); }); describe('.list()', () => { - mock.onGet('/methods').reply(200, response); - - it('should return a list of all methods', () => - methods.list().then(result => { + it('should return a list of all methods', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/methods', 200, response); + const result = await methods.list(); expect(result).toMatchSnapshot(); - })); + }); + }); it('should work with a callback', done => { - methods.list({}, (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchSnapshot(); - done(); + new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + networkMocker.intercept('GET', '/methods', 200, response); + methods.list({}, (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchSnapshot(); + done(); + }); }); }); }); From 454d66065697124f74c41fe1f4c34c9d8d4f71f5 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Thu, 1 Aug 2024 17:11:18 +0200 Subject: [PATCH 07/23] Rewrite networking tests to use Nock instead of the Axios adapter. --- tests/NetworkMocker.ts | 47 +-- .../custom-idempotency-key.test.ts | 2 +- tests/communication/request-retrying.test.ts | 2 +- tests/iteration/multipage-iteration.test.ts | 2 +- tests/unit/networking.test.ts | 307 ++++++++++-------- 5 files changed, 206 insertions(+), 154 deletions(-) diff --git a/tests/NetworkMocker.ts b/tests/NetworkMocker.ts index 01e9c1ca..25328165 100644 --- a/tests/NetworkMocker.ts +++ b/tests/NetworkMocker.ts @@ -68,42 +68,49 @@ class BaseNetworkMocker { } } -type InterceptParameters = Parameters['intercept']>; +type InterceptUri = Parameters['intercept']>[0]; +type NockUri = Parameters[0]; +type Uri = InterceptUri | { basePath: NockUri; path: InterceptUri }; type ReplyParameters = Parameters; /** * A helper for tests. It creates a Mollie Client, and activates and deactivates Nock. */ class NetworkMocker extends BaseNetworkMocker { - public readonly intercept: ( - method: InterceptParameters[1], - uri: InterceptParameters[0], - responseStatusCode: ReplyParameters[0], - responseBody?: ReplyParameters[1], - responseHeaders?: ReplyParameters[2], - ) => nock.Interceptor; + public readonly intercept: (method: string, uri: InterceptUri, responseStatusCode: number, responseBody?: nock.Body, responseHeaders?: nock.ReplyHeaders) => nock.Interceptor; + public readonly spy: (method: string, uri: InterceptUri, callback: (body: nock.Body, headers: Record>) => MaybePromise) => nock.Interceptor; constructor(clientProvider: () => MaybePromise) { super(clientProvider); - this.intercept = run( + const createInterceptor = run( nock('https://api.mollie.com:443/v2'), scope => - function intercept( - method: InterceptParameters[1], - uri: InterceptParameters[0], - responseStatusCode: ReplyParameters[0], - responseBody?: ReplyParameters[1], - responseHeaders?: ReplyParameters[2], - ) { - const interceptor = scope.intercept(uri, method); - interceptor.reply(responseStatusCode, responseBody, responseHeaders); - return interceptor; + function intercept(method: string, uri: Uri) { + if ('object' != typeof uri || uri instanceof RegExp) { + return scope.intercept(uri, method); + } /* if ('object' == typeof uri && false == uri instanceof RegExp) */ else { + return nock(uri.basePath).intercept(uri.path, method); + } }, ); + this.intercept = function intercept(method: string, uri: Uri, responseStatusCode: ReplyParameters[0], responseBody?: ReplyParameters[1], responseHeaders?: ReplyParameters[2]) { + const interceptor = createInterceptor(method, uri); + interceptor.reply(responseStatusCode, responseBody, responseHeaders); + return interceptor; + }; + this.spy = function spy(method: string, uri: Uri, callback: (body: nock.Body, headers: Record>) => MaybePromise) { + const interceptor = createInterceptor(method, uri); + interceptor.reply(function (_, body) { + return callback(body, this.req.headers as unknown as Record>); + }); + return interceptor; + }; } // Consider using Explicit Resource Management (https://github.com/tc39/proposal-explicit-resource-management). use(user: (usables: [mollieClient: MollieClient, networkMocker: NetworkMocker]) => MaybePromise) { - return this.prepare().then(mollieClient => user([mollieClient, this])).finally(this.cleanup); + return this.prepare() + .then(mollieClient => user([mollieClient, this])) + .finally(this.cleanup); } } diff --git a/tests/communication/custom-idempotency-key.test.ts b/tests/communication/custom-idempotency-key.test.ts index 2ad78644..0136bae8 100644 --- a/tests/communication/custom-idempotency-key.test.ts +++ b/tests/communication/custom-idempotency-key.test.ts @@ -49,7 +49,7 @@ const paymentResponse = { }; describe('custom-idempotency-key', () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider(true)); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); let mollieClient: MollieClient; beforeAll(async () => { diff --git a/tests/communication/request-retrying.test.ts b/tests/communication/request-retrying.test.ts index 8ed4f797..08e2addb 100644 --- a/tests/communication/request-retrying.test.ts +++ b/tests/communication/request-retrying.test.ts @@ -52,7 +52,7 @@ const paymentResponse = { }; describe('request-retrying', () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider(true)); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); let mollieClient: MollieClient; let intercept: (responseStatusCode: number, responseBody: Body, responseHeaders?: ReplyHeaders) => Interceptor; diff --git a/tests/iteration/multipage-iteration.test.ts b/tests/iteration/multipage-iteration.test.ts index 6bfd90c2..d6817133 100644 --- a/tests/iteration/multipage-iteration.test.ts +++ b/tests/iteration/multipage-iteration.test.ts @@ -6,7 +6,7 @@ import tick from '../tick'; import '../matchers/toBeDepleted'; describe('multipage-iteration', () => { - const networkMocker = new NetworkMocker(getApiKeyClientProvider(true)); + const networkMocker = new NetworkMocker(getApiKeyClientProvider()); let mollieClient: MollieClient; function intercept(limit: number, from?: number) { diff --git a/tests/unit/networking.test.ts b/tests/unit/networking.test.ts index 78b921e0..3e1dc80b 100644 --- a/tests/unit/networking.test.ts +++ b/tests/unit/networking.test.ts @@ -1,152 +1,197 @@ -import { MethodInclude } from '../..'; -import wireMockClient from '../wireMockClient'; - -test('queryString', async () => { - const { adapter, client } = wireMockClient(); - - adapter.onGet('/methods?include=issuers%2Cpricing&amount%5Bvalue%5D=10.00&amount%5Bcurrency%5D=SEK').reply(200, { - _embedded: { - methods: [], - }, - count: 0, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/methods-api/list-methods', - type: 'text/html', +import { apply } from 'ruply'; +import createMollieClient, { MethodInclude } from '../..'; +import NetworkMocker, { getApiKeyClientProvider } from '../NetworkMocker'; + +test('queryString', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker + .intercept('GET', '/methods?include=issuers%2Cpricing&amount%5Bvalue%5D=10.00&amount%5Bcurrency%5D=SEK', 200, { + _embedded: { + methods: [], + }, + count: 0, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/methods-api/list-methods', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/methods?include=issuers%2Cpricing&amount%5Bvalue%5D=10.00&amount%5Bcurrency%5D=SEK', + type: 'application/hal+json', + }, + }, + }) + .twice(); + + const methods = await bluster(mollieClient.methods.list.bind(mollieClient.methods))({ + include: [MethodInclude.issuers, MethodInclude.pricing], + amount: { + value: '10.00', + currency: 'SEK', }, - self: { - href: 'https://api.mollie.com/v2/methods?include=issuers%2Cpricing&amount%5Bvalue%5D=10.00&amount%5Bcurrency%5D=SEK', - type: 'application/hal+json', - }, - }, - }); + }); - const methods = await bluster(client.methods.list.bind(client.methods))({ - include: [MethodInclude.issuers, MethodInclude.pricing], - amount: { - value: '10.00', - currency: 'SEK', - }, + expect(methods.length).toBe(0); }); - - expect(methods.length).toBe(0); }); -test('defaults', async () => { - const { adapter, client } = wireMockClient(); - - adapter.onGet('/customers').reply(200, { - _embedded: { - customers: [], - }, - count: 0, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', - type: 'text/html', - }, - self: { - href: 'https://api.mollie.com/v2/customers?limit=50', - type: 'application/hal+json', - }, - previous: null, - next: null, - }, +test('defaults', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + let actualHeaders: Record> | undefined = undefined; + + networkMocker + .spy('GET', '/customers', (_, headers) => + apply( + [ + 200, + { + _embedded: { + customers: [], + }, + count: 0, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, + }, + }, + ], + () => (actualHeaders = headers), + ), + ) + .twice(); + + await bluster(mollieClient.customers.page.bind(mollieClient.customers))(); + + // Test previously named "it should have some default headers set". + apply( + actualHeaders!!['authorization'], + header => expect(header).toHaveLength(1), + header => expect(header[0]).toBe('Bearer test_mock'), + ); + apply( + actualHeaders!!['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0]).toMatch(/^Node\/[v\d\.]+ Mollie\/[v\d\.]+(?:-[\w\.]+)?$/i), + ); + apply( + actualHeaders!!['accept-encoding'], + header => expect(header).toHaveLength(1), + header => expect(header[0]).toBe('gzip'), + ); + apply( + actualHeaders!!['content-type'], + header => expect(header).toHaveLength(1), + header => expect(header[0]).toBe('application/json'), + ); }); - - await bluster(client.customers.page.bind(client.customers))(); - - const { baseURL, headers, httpsAgent } = adapter.history.get[0]; - - // Test previously named "it should have a secure baseURL set". - expect(baseURL).toBe('https://api.mollie.com:443/v2/'); - - // Test previously named "it should have some default headers set". - expect(headers['Authorization']).toBe('Bearer mock-api-key'); - expect(/^Node\/[v\d\.]+ Mollie\/[v\d\.]+(?:-[\w\.]+)?$/i.test(headers['User-Agent'])).toBe(true); - expect(headers['Accept-Encoding']).toBe('gzip'); - expect(headers['Content-Type']).toBe('application/json'); - - // Test previously named "it should have a custom httpsAgent with cert loaded". - expect(httpsAgent).toBeTruthy(); }); -async function requestWithVersionStrings(versionStrings: string | string[] | undefined) { - const { adapter, client } = wireMockClient({ versionStrings }); - - adapter.onGet('/customers').reply(200, { - _embedded: { - customers: [], - }, - count: 0, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', - type: 'text/html', - }, - self: { - href: 'https://api.mollie.com/v2/customers?limit=50', - type: 'application/hal+json', - }, - previous: null, - next: null, - }, +async function requestWithVersionStrings(versionStrings: string): Promise>> { + return new NetworkMocker(createMollieClient.bind(undefined, { apiKey: 'test_mock', versionStrings })).use(async ([mollieClient, networkMocker]) => { + let actualHeaders: Record> | undefined = undefined; + + networkMocker + .spy('GET', '/customers', (_, headers) => + apply( + [ + 200, + { + _embedded: { + customers: [], + }, + count: 0, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, + }, + }, + ], + () => (actualHeaders = headers), + ), + ) + .twice(); + + await bluster(mollieClient.customers.page.bind(mollieClient.customers))(); + + return actualHeaders; }); - - await bluster(client.customers.page.bind(client.customers))(); - - return adapter.history.get[0]; } test('customVersionStrings', async () => { // Test previously named "it should add a version string". - let headers = (await requestWithVersionStrings('ReactionCommerce/1.16.0')).headers; - - expect(headers['User-Agent'].endsWith('ReactionCommerce/1.16.0')).toBe(true); - - // Test previously named "it should accept a version string with whitespace". - headers = (await requestWithVersionStrings('Reaction Commerce/1.16.0')).headers; - - expect(headers['User-Agent'].endsWith('ReactionCommerce/1.16.0')).toBe(true); + apply( + (await requestWithVersionStrings('ReactionCommerce/1.16.0'))['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0].endsWith('ReactionCommerce/1.16.0')).toBeTruthy(), + ); + + // Test previously named "it should add a version string with whitespace". + apply( + (await requestWithVersionStrings('Reaction Commerce/1.16.0'))['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0].endsWith('ReactionCommerce/1.16.0')).toBeTruthy(), + ); // Test previously named "it should not camelCase all uppercase version strings". - headers = (await requestWithVersionStrings('PHP/7.3.4')).headers; - - expect(headers['User-Agent'].endsWith('PHP/7.3.4')).toBe(true); + apply( + (await requestWithVersionStrings('PHP/7.3.4'))['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0].endsWith('PHP/7.3.4')).toBeTruthy(), + ); // Test previously named "it should not camelCase all uppercase version strings with whitespace". - headers = (await requestWithVersionStrings('PHP COOKBOOK FOR NODE USERS/7.3.4')).headers; - - expect(headers['User-Agent'].endsWith('PHPCOOKBOOKFORNODEUSERS/7.3.4')).toBe(true); - - headers = (await requestWithVersionStrings('php cookbook for node users/7.3.4')).headers; - - expect(headers['User-Agent'].endsWith('phpCookbookForNodeUsers/7.3.4')).toBe(true); + apply( + (await requestWithVersionStrings('PHP COOKBOOK FOR NODE USERS/7.3.4'))['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0].endsWith('PHPCOOKBOOKFORNODEUSERS/7.3.4')).toBeTruthy(), + ); + + apply( + (await requestWithVersionStrings('php cookbook for node users/7.3.4'))['user-agent'], + header => expect(header).toHaveLength(1), + header => expect(header[0].endsWith('phpCookbookForNodeUsers/7.3.4')).toBeTruthy(), + ); }); -test('customApiEndpoint', async () => { - const { adapter, client } = wireMockClient({ apiEndpoint: 'https://null.house/' }); - - adapter.onGet('/customers').reply(200, { - _embedded: { - customers: [], - }, - count: 0, - _links: { - documentation: { - href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', - type: 'text/html', - }, - self: { - href: 'https://api.mollie.com/v2/customers?limit=50', - type: 'application/hal+json', - }, - previous: null, - next: null, - }, +test('customApiEndpoint', () => { + return new NetworkMocker(createMollieClient.bind(undefined, { apiKey: 'test_mock', apiEndpoint: 'https://null.house/' })).use(([mollieClient, networkMocker]) => { + networkMocker + .intercept('GET', { basePath: 'https://null.house', path: '/customers' }, 200, { + _embedded: { + customers: [], + }, + count: 0, + _links: { + documentation: { + href: 'https://docs.mollie.com/reference/v2/customers-api/list-customers', + type: 'text/html', + }, + self: { + href: 'https://api.mollie.com/v2/customers?limit=50', + type: 'application/hal+json', + }, + previous: null, + next: null, + }, + }) + .twice(); + + return bluster(mollieClient.customers.page.bind(mollieClient.customers))(); }); - - await bluster(client.customers.page.bind(client.customers))(); - - expect(adapter.history.get[0].baseURL).toBe('https://null.house/'); }); From 66d08fa02ad6e694beb625ae1d2deebce437c4c2 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 18 Aug 2024 22:32:41 +0200 Subject: [PATCH 08/23] Rewrite inspect tests to use Nock instead of the Axios adapter. --- tests/unit/inspect.test.ts | 88 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/tests/unit/inspect.test.ts b/tests/unit/inspect.test.ts index 5db33f23..d15ec080 100644 --- a/tests/unit/inspect.test.ts +++ b/tests/unit/inspect.test.ts @@ -1,51 +1,53 @@ import { inspect } from 'util'; -import wireMockClient from '../wireMockClient'; import '../matchers/toStartWith'; +import NetworkMocker, { getApiKeyClientProvider } from '../NetworkMocker'; -test('inspect', async () => { - const { adapter, client } = wireMockClient(); - - adapter.onGet('/methods/ideal').reply(200, { - resource: 'method', - id: 'ideal', - description: 'iDEAL', - minimumAmount: { - value: '0.01', - currency: 'EUR', - }, - maximumAmount: { - value: '50000.00', - currency: 'EUR', - }, - image: { - size1x: 'https://www.mollie.com/external/icons/payment-methods/ideal.png', - size2x: 'https://www.mollie.com/external/icons/payment-methods/ideal%402x.png', - svg: 'https://www.mollie.com/external/icons/payment-methods/ideal.svg', - }, - status: 'activated', - pricing: [ - { - description: 'Netherlands', - fixed: { - value: '0.29', +test('inspect', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + networkMocker + .intercept('GET', '/methods/ideal', 200, { + resource: 'method', + id: 'ideal', + description: 'iDEAL', + minimumAmount: { + value: '0.01', currency: 'EUR', }, - variable: '0', - }, - ], - _links: { - self: { - href: 'https://api.mollie.com/v2/methods/ideal?include=pricing', - type: 'application/hal+json', - }, - documentation: { - href: 'https://docs.mollie.com/reference/v2/methods-api/get-method', - type: 'text/html', - }, - }, - }); + maximumAmount: { + value: '50000.00', + currency: 'EUR', + }, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/ideal.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/ideal%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/ideal.svg', + }, + status: 'activated', + pricing: [ + { + description: 'Netherlands', + fixed: { + value: '0.29', + currency: 'EUR', + }, + variable: '0', + }, + ], + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/ideal?include=pricing', + type: 'application/hal+json', + }, + documentation: { + href: 'https://docs.mollie.com/reference/v2/methods-api/get-method', + type: 'text/html', + }, + }, + }) + .twice(); - const method = await client.methods.get('ideal'); + const method = await mollieClient.methods.get('ideal'); - expect(inspect(method)).toStartWith('Method ideal {'); + expect(inspect(method)).toStartWith('Method ideal {'); + }); }); From c43aeaa0f54a54457e252eb14b8373e8a9e6424a Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 18 Aug 2024 22:36:28 +0200 Subject: [PATCH 09/23] Change behaviour of NetworkClient for DELETE requests without body. In 3.7.0, DELETE requests without body sometimes cause "{}" to be sent, and sometimes causes no body to be sent at all. Demian changed this, causing no body to be sent consistently. This commit causes "{}" to be sent consistently. --- src/communication/NetworkClient.ts | 2 +- tests/__nock-fixtures__/profiles.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 261e4a01..bb7da2aa 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -251,7 +251,7 @@ export default class NetworkClient { const config: RequestInit = { method: 'DELETE', headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, - body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined, + body: JSON.stringify(body), }; return this.request(buildUrl(pathname), config).catch(throwApiError).then(processFetchResponse); } diff --git a/tests/__nock-fixtures__/profiles.json b/tests/__nock-fixtures__/profiles.json index 17ca4e10..bc6cf08b 100644 --- a/tests/__nock-fixtures__/profiles.json +++ b/tests/__nock-fixtures__/profiles.json @@ -326,7 +326,7 @@ "scope": "https://api.mollie.com:443", "method": "DELETE", "path": "/v2/profiles/pfl_XjBqKLWD7q", - "body": "", + "body": {}, "status": 204, "response": "", "rawHeaders": [ From ebc42b24ee55795c80473472095d4864fd0dd5fc Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 18 Aug 2024 23:20:57 +0200 Subject: [PATCH 10/23] Fix method (unit) tests. --- tests/unit/resources/methods.test.ts | 48 ++++++++++++++++------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/tests/unit/resources/methods.test.ts b/tests/unit/resources/methods.test.ts index f41f38d4..3d9ac7fa 100644 --- a/tests/unit/resources/methods.test.ts +++ b/tests/unit/resources/methods.test.ts @@ -23,20 +23,22 @@ describe('methods', () => { }); }); - it('should work with a callback', done => { - new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + it('should work with a callback', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(([, networkMocker]) => { networkMocker.intercept('GET', `/methods/${methodId}`, 200, response._embedded.methods[0]); - methods.get(methodId, {}, (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchSnapshot(); - done(); + return new Promise(resolve => { + methods.get(methodId, {}, (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchSnapshot(); + resolve(); + }); }); }); }); it('should throw an error for non-existent IDs', () => { return new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { - networkMocker.intercept('GET', '/methods/foo', 500, error); + networkMocker.intercept('GET', '/methods/foo', 500, error).thrice(); await methods .get('foo') .then(() => { @@ -49,14 +51,16 @@ describe('methods', () => { }); }); - it('should return an error with a callback for non-existent IDs', done => { - new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { - networkMocker.intercept('GET', '/methods/foo', 500, error); - methods.get('foo', {}, (err: any, result) => { - expect(err).toBeInstanceOf(ApiError); - expect(err.getMessage()).toEqual(error.detail); - expect(result).toBeUndefined(); - done(); + it('should return an error with a callback for non-existent IDs', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(([, networkMocker]) => { + networkMocker.intercept('GET', '/methods/foo', 500, error).thrice(); + return new Promise(resolve => { + methods.get('foo', {}, (err: any, result) => { + expect(err).toBeInstanceOf(ApiError); + expect(err.getMessage()).toEqual(error.detail); + expect(result).toBeUndefined(); + resolve(); + }); }); }); }); @@ -71,13 +75,15 @@ describe('methods', () => { }); }); - it('should work with a callback', done => { - new NetworkMocker(getApiKeyClientProvider()).use(async ([, networkMocker]) => { + it('should work with a callback', () => { + return new NetworkMocker(getApiKeyClientProvider()).use(([, networkMocker]) => { networkMocker.intercept('GET', '/methods', 200, response); - methods.list({}, (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchSnapshot(); - done(); + return new Promise(resolve => { + methods.list({}, (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchSnapshot(); + resolve(); + }); }); }); }); From cae8fef89eff53fd5c8366626beb3afd49a6920b Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 18 Aug 2024 23:36:53 +0200 Subject: [PATCH 11/23] Move Axios dependency to dev dependencies, as it is merely used in the test suite. Also, removed the Axios mock adapter dependency completely. --- package.json | 3 +-- yarn.lock | 31 +++++++++---------------------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 23a9e466..80f87ac9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "lint": "yarn lint:eslint:fix && yarn lint:prettier" }, "dependencies": { - "axios": "^1.6.2", "node-fetch": "^2.7.0", "ruply": "^1.0.1" }, @@ -53,7 +52,7 @@ "@types/jest": "^29.5.11", "@types/node": "^18.14.6", "@types/node-fetch": "^2.6.11", - "axios-mock-adapter": "1.19.0", + "axios": "^1.7.4", "babel-jest": "^29.5.0", "commitizen": "^4.3.0", "cz-conventional-changelog": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index fa88743c..99b4bada 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1854,20 +1854,12 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios-mock-adapter@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4" - integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg== +axios@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== dependencies: - fast-deep-equal "^3.1.3" - is-buffer "^2.0.3" - -axios@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== - dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -2845,10 +2837,10 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-each@^0.3.3: version "0.3.3" @@ -3239,11 +3231,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" From e35da05ac9c17b35c0957c3eb4ee21f64da49073 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 18 Aug 2024 23:41:46 +0200 Subject: [PATCH 12/23] =?UTF-8?q?Lower=20Node.js=20version=20requirement?= =?UTF-8?q?=20to=208.=C3=97.=C3=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node-fetch supports 6.×.× (and even 4; but not 5). However, having the version requirement at 8.×.× makes testing easier, as some testing utilities don't support older versions. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80f87ac9..8f9efa3b 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "jsnext:main": "dist/mollie.esm.js", "types": "dist/types/src/types.d.ts", "engines": { - "node": ">=14" + "node": ">=8" }, "scripts": { "prepublish": "yarn build", From c9478c96d69b7815c0ddfcd75a0cc97745f66141 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Fri, 6 Sep 2024 12:19:03 +0200 Subject: [PATCH 13/23] Refactor processFetchResponse slightly. --- src/communication/NetworkClient.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index bb7da2aa..2a2a79bd 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -73,16 +73,20 @@ const throwApiError = run( ); /** - * Checks if an API error needs to be thrown based on the passed result. + * Checks whether an API error needs to be thrown based on the passed result. */ -async function processFetchResponse(res: ResponseWithIdempotencyKey) { +async function processFetchResponse(response: ResponseWithIdempotencyKey) { // Request was successful, but no content was returned. - if (res.status == 204) return true; + if (response.status == 204) return true; // Request was successful and content was returned. - const body = await res.json(); - if (res.status >= 200 && res.status < 300) return body; + const body = await response.json(); + if (Math.floor(response.status / 100) == 2) { + return body; + } // Request was not successful, but the response body contains an error message. - if (body) throw ApiError.createFromResponse(body, res.idempotencyKey); + if (body) { + throw ApiError.createFromResponse(body, response.idempotencyKey); + } // Request was not successful. throw new ApiError('An unknown error has occurred'); } From 733259f1fc989cf3144ba67187ddb153f7a5cef8 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Fri, 6 Sep 2024 12:34:27 +0200 Subject: [PATCH 14/23] Use fetch in getAccessTokenClientProvider instead of Axios. --- package.json | 1 - tests/NetworkMocker.ts | 16 +++++++--------- yarn.lock | 19 ------------------- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 8f9efa3b..a0311d44 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "@types/jest": "^29.5.11", "@types/node": "^18.14.6", "@types/node-fetch": "^2.6.11", - "axios": "^1.7.4", "babel-jest": "^29.5.0", "commitizen": "^4.3.0", "cz-conventional-changelog": "^3.3.0", diff --git a/tests/NetworkMocker.ts b/tests/NetworkMocker.ts index 25328165..c9e0c2ef 100644 --- a/tests/NetworkMocker.ts +++ b/tests/NetworkMocker.ts @@ -1,7 +1,7 @@ -import axios from 'axios'; import dotenv from 'dotenv'; import nock, { Interceptor } from 'nock'; import { setupRecorder } from 'nock-record'; +import fetch from 'node-fetch'; import { apply, run } from 'ruply'; import createMollieClient, { MollieClient } from '..'; import fling from '../src/plumbing/fling'; @@ -36,14 +36,12 @@ export function getAccessTokenClientProvider(mockAuthorization = true) { oauthAuthorizationHeaderValue: `Basic: ${Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`, refreshToken: REFRESH_TOKEN, })); - const { data } = await axios.post( - 'https://api.mollie.com/oauth2/tokens', - { - grant_type: 'refresh_token', - refresh_token: refreshToken, - }, - { headers: { Authorization: oauthAuthorizationHeaderValue } }, - ); + const response = await fetch('https://api.mollie.com/oauth2/tokens', { + method: 'POST', + headers: { Authorization: oauthAuthorizationHeaderValue }, + body: JSON.stringify({ grant_type: 'refresh_token', refresh_token: refreshToken }), + }); + const data = await response.json(); const accessToken: string = data['access_token']; return apply(createMollieClient({ accessToken }), client => ((client as { accessToken?: string }).accessToken = accessToken)); }; diff --git a/yarn.lock b/yarn.lock index 99b4bada..99878313 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1854,15 +1854,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" - integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - babel-jest@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" @@ -2837,11 +2828,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -4383,11 +4369,6 @@ propagate@^2.0.0: resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" From d468772d12cb078f862af90b29678256aac22684 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Fri, 6 Sep 2024 12:37:33 +0200 Subject: [PATCH 15/23] Remove wireMockClient. --- tests/wireMockClient.ts | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 tests/wireMockClient.ts diff --git a/tests/wireMockClient.ts b/tests/wireMockClient.ts deleted file mode 100644 index 3e739caf..00000000 --- a/tests/wireMockClient.ts +++ /dev/null @@ -1,16 +0,0 @@ -import createMollieClient, { MollieClient, MollieOptions } from '..'; -import { AxiosInstance } from 'axios'; -import MockAdapter from 'axios-mock-adapter'; -import 'jest-bluster'; - -export default function wireMockClient(options?: Omit): { adapter: MockAdapter; client: MollieClient } { - const adapter = new MockAdapter(undefined as unknown as AxiosInstance); - return { - adapter, - client: createMollieClient({ - apiKey: 'mock-api-key', - adapter: adapter.adapter(), - ...options, - }), - }; -} From c23df8a5f02c69b9b3dd1790390b6ebdc08bf00b Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Fri, 6 Sep 2024 14:50:29 +0200 Subject: [PATCH 16/23] revert pathnames to have no starting slash --- src/binders/applePay/ApplePayBinder.ts | 2 +- src/binders/chargebacks/ChargebacksBinder.ts | 2 +- src/binders/customers/CustomersBinder.ts | 2 +- src/binders/customers/mandates/CustomerMandatesBinder.ts | 2 +- src/binders/customers/payments/CustomerPaymentsBinder.ts | 2 +- .../customers/subscriptions/CustomerSubscriptionsBinder.ts | 2 +- src/binders/methods/MethodsBinder.ts | 2 +- src/binders/onboarding/OnboardingBinder.ts | 2 +- src/binders/orders/OrdersBinder.ts | 2 +- src/binders/orders/orderlines/OrderLinesBinder.ts | 2 +- src/binders/orders/shipments/OrderShipmentsBinder.ts | 2 +- src/binders/organizations/OrganizationsBinder.ts | 2 +- src/binders/paymentLinks/PaymentLinksBinder.ts | 2 +- src/binders/payments/PaymentsBinder.ts | 2 +- src/binders/payments/captures/PaymentCapturesBinder.ts | 2 +- .../payments/chargebacks/PaymentChargebacksBinder.ts | 2 +- src/binders/payments/orders/OrderPaymentsBinder.ts | 2 +- src/binders/payments/refunds/PaymentRefundsBinder.ts | 2 +- src/binders/permissions/PermissionsBinder.ts | 2 +- src/binders/profiles/ProfilesBinder.ts | 2 +- .../giftcardIssuers/ProfileGiftcardIssuersBinder.ts | 2 +- src/binders/profiles/methods/ProfileMethodsBinder.ts | 2 +- .../profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts | 2 +- src/binders/refunds/RefundsBinder.ts | 2 +- src/binders/refunds/orders/OrderRefundsBinder.ts | 2 +- src/binders/settlements/SettlementsBinder.ts | 2 +- .../settlements/captures/SettlementCapturesBinder.ts | 2 +- .../settlements/chargebacks/SettlementChargebacksBinder.ts | 2 +- .../settlements/payments/SettlementPaymentsBinder.ts | 2 +- src/binders/settlements/refunds/SettlementRefundsBinder.ts | 2 +- src/binders/subscriptions/SubscriptionsBinder.ts | 2 +- .../subscriptions/payments/SubscriptionPaymentsBinder.ts | 2 +- src/communication/NetworkClient.ts | 3 +-- src/communication/makeRetrying.ts | 6 +++--- 34 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/binders/applePay/ApplePayBinder.ts b/src/binders/applePay/ApplePayBinder.ts index 17117cba..11775b91 100644 --- a/src/binders/applePay/ApplePayBinder.ts +++ b/src/binders/applePay/ApplePayBinder.ts @@ -4,7 +4,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import { type RequestPaymentSessionParameters } from './parameters'; -const pathSegments = '/wallets/applepay/sessions'; +const pathSegments = 'wallets/applepay/sessions'; export default class ApplePayBinder { constructor(protected readonly networkClient: NetworkClient) {} diff --git a/src/binders/chargebacks/ChargebacksBinder.ts b/src/binders/chargebacks/ChargebacksBinder.ts index a58fab60..d71569e5 100644 --- a/src/binders/chargebacks/ChargebacksBinder.ts +++ b/src/binders/chargebacks/ChargebacksBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = '/chargebacks'; +const pathSegment = 'chargebacks'; export default class ChargebacksBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/customers/CustomersBinder.ts b/src/binders/customers/CustomersBinder.ts index d8d99672..a26f949a 100644 --- a/src/binders/customers/CustomersBinder.ts +++ b/src/binders/customers/CustomersBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type DeleteParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = '/customers'; +const pathSegment = 'customers'; export default class CustomersBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/customers/mandates/CustomerMandatesBinder.ts b/src/binders/customers/mandates/CustomerMandatesBinder.ts index 41d64c98..95633ad7 100644 --- a/src/binders/customers/mandates/CustomerMandatesBinder.ts +++ b/src/binders/customers/mandates/CustomerMandatesBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type RevokeParameters } from './parameters'; function getPathSegments(customerId: string) { - return `/customers/${customerId}/mandates`; + return `customers/${customerId}/mandates`; } export default class CustomerMandatesBinder extends Binder { diff --git a/src/binders/customers/payments/CustomerPaymentsBinder.ts b/src/binders/customers/payments/CustomerPaymentsBinder.ts index e00187f0..420da5ff 100644 --- a/src/binders/customers/payments/CustomerPaymentsBinder.ts +++ b/src/binders/customers/payments/CustomerPaymentsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(customerId: string) { - return `/customers/${customerId}/payments`; + return `customers/${customerId}/payments`; } export default class CustomerPaymentsBinder extends Binder { diff --git a/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts b/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts index 3c79538c..e1816103 100644 --- a/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts +++ b/src/binders/customers/subscriptions/CustomerSubscriptionsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; function getPathSegments(customerId: string) { - return `/customers/${customerId}/subscriptions`; + return `customers/${customerId}/subscriptions`; } export default class CustomerSubscriptionsBinder extends Binder { diff --git a/src/binders/methods/MethodsBinder.ts b/src/binders/methods/MethodsBinder.ts index 881b93ae..e178a9da 100644 --- a/src/binders/methods/MethodsBinder.ts +++ b/src/binders/methods/MethodsBinder.ts @@ -7,7 +7,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type GetParameters, type ListParameters } from './parameters'; -const pathSegment = '/methods'; +const pathSegment = 'methods'; export default class MethodsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/onboarding/OnboardingBinder.ts b/src/binders/onboarding/OnboardingBinder.ts index df3a61db..d70337fd 100644 --- a/src/binders/onboarding/OnboardingBinder.ts +++ b/src/binders/onboarding/OnboardingBinder.ts @@ -6,7 +6,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type SubmitParameters } from './parameters'; -const pathSegments = '/onboarding/me'; +const pathSegments = 'onboarding/me'; export default class OnboardingBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/orders/OrdersBinder.ts b/src/binders/orders/OrdersBinder.ts index bd211fc9..c5101f6b 100644 --- a/src/binders/orders/OrdersBinder.ts +++ b/src/binders/orders/OrdersBinder.ts @@ -10,7 +10,7 @@ import Binder from '../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; import alias from '../../plumbing/alias'; -export const pathSegment = '/orders'; +export const pathSegment = 'orders'; /** * The **Orders API** allows you to use Mollie for your order management. diff --git a/src/binders/orders/orderlines/OrderLinesBinder.ts b/src/binders/orders/orderlines/OrderLinesBinder.ts index c88e2256..f97cadcd 100644 --- a/src/binders/orders/orderlines/OrderLinesBinder.ts +++ b/src/binders/orders/orderlines/OrderLinesBinder.ts @@ -10,7 +10,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type UpdateParameters } from './parameters'; function getPathSegments(orderId: string) { - return `/orders/${orderId}/lines`; + return `orders/${orderId}/lines`; } export default class OrderLinesBinder extends Binder { diff --git a/src/binders/orders/shipments/OrderShipmentsBinder.ts b/src/binders/orders/shipments/OrderShipmentsBinder.ts index 47ccdc23..5ac01fee 100644 --- a/src/binders/orders/shipments/OrderShipmentsBinder.ts +++ b/src/binders/orders/shipments/OrderShipmentsBinder.ts @@ -10,7 +10,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type GetParameters, type ListParameters, type UpdateParameters } from './parameters'; export function getPathSegments(orderId: string) { - return `/orders/${orderId}/shipments`; + return `orders/${orderId}/shipments`; } export default class OrderShipmentsBinder extends Binder { diff --git a/src/binders/organizations/OrganizationsBinder.ts b/src/binders/organizations/OrganizationsBinder.ts index fda0158e..79ff7541 100644 --- a/src/binders/organizations/OrganizationsBinder.ts +++ b/src/binders/organizations/OrganizationsBinder.ts @@ -7,7 +7,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import Binder from '../Binder'; -const pathSegment = '/organizations'; +const pathSegment = 'organizations'; export default class OrganizationsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/paymentLinks/PaymentLinksBinder.ts b/src/binders/paymentLinks/PaymentLinksBinder.ts index 68c1ba48..3f155fa3 100644 --- a/src/binders/paymentLinks/PaymentLinksBinder.ts +++ b/src/binders/paymentLinks/PaymentLinksBinder.ts @@ -9,7 +9,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type GetParameters, type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = '/payment-links'; +const pathSegment = 'payment-links'; export default class PaymentsLinksBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/payments/PaymentsBinder.ts b/src/binders/payments/PaymentsBinder.ts index d9509e0b..17f9feac 100644 --- a/src/binders/payments/PaymentsBinder.ts +++ b/src/binders/payments/PaymentsBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = '/payments'; +const pathSegment = 'payments'; export default class PaymentsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/payments/captures/PaymentCapturesBinder.ts b/src/binders/payments/captures/PaymentCapturesBinder.ts index 50d30a33..2bec810e 100644 --- a/src/binders/payments/captures/PaymentCapturesBinder.ts +++ b/src/binders/payments/captures/PaymentCapturesBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `/payments/${paymentId}/captures`; + return `payments/${paymentId}/captures`; } export default class PaymentCapturesBinder extends Binder { diff --git a/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts b/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts index 9d215484..521a27ac 100644 --- a/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts +++ b/src/binders/payments/chargebacks/PaymentChargebacksBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `/payments/${paymentId}/chargebacks`; + return `payments/${paymentId}/chargebacks`; } export default class PaymentChargebacksBinder extends Binder { diff --git a/src/binders/payments/orders/OrderPaymentsBinder.ts b/src/binders/payments/orders/OrderPaymentsBinder.ts index a79cd4ad..2ed1e7ef 100644 --- a/src/binders/payments/orders/OrderPaymentsBinder.ts +++ b/src/binders/payments/orders/OrderPaymentsBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type CreateParameters } from './parameters'; function getPathSegments(orderId: string) { - return `/orders/${orderId}/payments`; + return `orders/${orderId}/payments`; } export default class OrderPaymentsBinder extends Binder { diff --git a/src/binders/payments/refunds/PaymentRefundsBinder.ts b/src/binders/payments/refunds/PaymentRefundsBinder.ts index 4a393f00..c223436b 100644 --- a/src/binders/payments/refunds/PaymentRefundsBinder.ts +++ b/src/binders/payments/refunds/PaymentRefundsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CancelParameters, type CreateParameters, type GetParameters, type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(paymentId: string) { - return `/payments/${paymentId}/refunds`; + return `payments/${paymentId}/refunds`; } export default class PaymentRefundsBinder extends Binder { diff --git a/src/binders/permissions/PermissionsBinder.ts b/src/binders/permissions/PermissionsBinder.ts index 93312ee9..3e3478da 100644 --- a/src/binders/permissions/PermissionsBinder.ts +++ b/src/binders/permissions/PermissionsBinder.ts @@ -6,7 +6,7 @@ import renege from '../../plumbing/renege'; import type Callback from '../../types/Callback'; import Binder from '../Binder'; -const pathSegment = '/permissions'; +const pathSegment = 'permissions'; export default class PermissionsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/profiles/ProfilesBinder.ts b/src/binders/profiles/ProfilesBinder.ts index 924ebec0..24763900 100644 --- a/src/binders/profiles/ProfilesBinder.ts +++ b/src/binders/profiles/ProfilesBinder.ts @@ -10,7 +10,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type CreateParameters, type DeleteParameters, type IterateParameters, type PageParameters, type UpdateParameters } from './parameters'; -const pathSegment = '/profiles'; +const pathSegment = 'profiles'; export default class ProfilesBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts index 298b5d10..6311ebcb 100644 --- a/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts +++ b/src/binders/profiles/giftcardIssuers/ProfileGiftcardIssuersBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `/profiles/${profileId}/methods/giftcard/issuers`; + return `profiles/${profileId}/methods/giftcard/issuers`; } export default class ProfileGiftcardIssuersBinder extends Binder { diff --git a/src/binders/profiles/methods/ProfileMethodsBinder.ts b/src/binders/profiles/methods/ProfileMethodsBinder.ts index 887fc6ec..026705cc 100644 --- a/src/binders/profiles/methods/ProfileMethodsBinder.ts +++ b/src/binders/profiles/methods/ProfileMethodsBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `/profiles/${profileId}/methods`; + return `profiles/${profileId}/methods`; } export default class ProfileMethodsBinder extends Binder { diff --git a/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts b/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts index c89a5fd2..3a1b924d 100644 --- a/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts +++ b/src/binders/profiles/voucherIssuers/ProfileVoucherIssuersBinder.ts @@ -9,7 +9,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type Parameters } from './parameters'; function getPathSegments(profileId: string) { - return `/profiles/${profileId}/methods/voucher/issuers`; + return `profiles/${profileId}/methods/voucher/issuers`; } export default class ProfileVoucherIssuersBinder extends Binder { diff --git a/src/binders/refunds/RefundsBinder.ts b/src/binders/refunds/RefundsBinder.ts index 30760412..4611f441 100644 --- a/src/binders/refunds/RefundsBinder.ts +++ b/src/binders/refunds/RefundsBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = '/refunds'; +const pathSegment = 'refunds'; export default class RefundsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/refunds/orders/OrderRefundsBinder.ts b/src/binders/refunds/orders/OrderRefundsBinder.ts index 0095de80..5d44159b 100644 --- a/src/binders/refunds/orders/OrderRefundsBinder.ts +++ b/src/binders/refunds/orders/OrderRefundsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type CreateParameters, type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(orderId: string) { - return `/orders/${orderId}/refunds`; + return `orders/${orderId}/refunds`; } export default class OrderRefundsBinder extends Binder { diff --git a/src/binders/settlements/SettlementsBinder.ts b/src/binders/settlements/SettlementsBinder.ts index d8736250..4d94fbe1 100644 --- a/src/binders/settlements/SettlementsBinder.ts +++ b/src/binders/settlements/SettlementsBinder.ts @@ -7,7 +7,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = '/settlements'; +const pathSegment = 'settlements'; export default class SettlementsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/settlements/captures/SettlementCapturesBinder.ts b/src/binders/settlements/captures/SettlementCapturesBinder.ts index b9420c30..61f66357 100644 --- a/src/binders/settlements/captures/SettlementCapturesBinder.ts +++ b/src/binders/settlements/captures/SettlementCapturesBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `/settlements/${settlementId}/captures`; + return `settlements/${settlementId}/captures`; } export default class SettlementCapturesBinder extends Binder { diff --git a/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts b/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts index 4a1ec5fb..60e14bc2 100644 --- a/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts +++ b/src/binders/settlements/chargebacks/SettlementChargebacksBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `/settlements/${settlementId}/chargebacks`; + return `settlements/${settlementId}/chargebacks`; } export default class SettlementChargebacksBinder extends Binder { diff --git a/src/binders/settlements/payments/SettlementPaymentsBinder.ts b/src/binders/settlements/payments/SettlementPaymentsBinder.ts index bd9bdd07..9783eb11 100644 --- a/src/binders/settlements/payments/SettlementPaymentsBinder.ts +++ b/src/binders/settlements/payments/SettlementPaymentsBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `/settlements/${settlementId}/payments`; + return `settlements/${settlementId}/payments`; } export default class SettlementPaymentsBinder extends Binder { diff --git a/src/binders/settlements/refunds/SettlementRefundsBinder.ts b/src/binders/settlements/refunds/SettlementRefundsBinder.ts index 4b53c92c..68b71d00 100644 --- a/src/binders/settlements/refunds/SettlementRefundsBinder.ts +++ b/src/binders/settlements/refunds/SettlementRefundsBinder.ts @@ -8,7 +8,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; export function getPathSegments(settlementId: string) { - return `/settlements/${settlementId}/refunds`; + return `settlements/${settlementId}/refunds`; } export default class SettlementRefundsBinder extends Binder { diff --git a/src/binders/subscriptions/SubscriptionsBinder.ts b/src/binders/subscriptions/SubscriptionsBinder.ts index 7b40edcd..a7a1a810 100644 --- a/src/binders/subscriptions/SubscriptionsBinder.ts +++ b/src/binders/subscriptions/SubscriptionsBinder.ts @@ -8,7 +8,7 @@ import type Callback from '../../types/Callback'; import Binder from '../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; -const pathSegment = '/subscriptions'; +const pathSegment = 'subscriptions'; export default class SubscriptionsBinder extends Binder { constructor(protected readonly networkClient: TransformingNetworkClient) { diff --git a/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts b/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts index a3fabd29..1c3643e8 100644 --- a/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts +++ b/src/binders/subscriptions/payments/SubscriptionPaymentsBinder.ts @@ -11,7 +11,7 @@ import Binder from '../../Binder'; import { type IterateParameters, type PageParameters } from './parameters'; function getPathSegments(customerId: string, subscriptionId: string): string { - return `/customers/${customerId}/subscriptions/${subscriptionId}/payments`; + return `customers/${customerId}/subscriptions/${subscriptionId}/payments`; } export default class SubscriptionPaymentsBinder extends Binder { diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 2a2a79bd..f5830d8e 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -129,8 +129,7 @@ export default class NetworkClient { // Create the request function. this.request = (pathname, options) => { - // If the pathname starts with a slash, remove it and prepend the API endpoint. - const url = pathname.startsWith('/') ? `${apiEndpoint}${pathname.substring(1)}` : pathname; + const url = new URL(pathname, apiEndpoint); return fetchWithRetries(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); }; } diff --git a/src/communication/makeRetrying.ts b/src/communication/makeRetrying.ts index 91a7fb1e..6958fe70 100644 --- a/src/communication/makeRetrying.ts +++ b/src/communication/makeRetrying.ts @@ -1,6 +1,6 @@ import { randomBytes } from 'crypto'; -import fetch, { type RequestInit, type Response } from 'node-fetch'; +import fetch, { type RequestInit, type Response, type RequestInfo } from 'node-fetch'; /** * The delay, in milliseconds, between attempts. @@ -67,14 +67,14 @@ export type ResponseWithIdempotencyKey = Response & { idempotencyKey: string | u * safely re-attempt requests. */ export function retryingFetch(originalFetch: typeof fetch) { - return async function fetchWithRetries(input: string, init: RequestInit): Promise { + return async function fetchWithRetries(url: RequestInfo, init: RequestInit): Promise { // If the request is a POST or DELETE one and does not yet have the idempotency header, add one now. if (init.method != undefined && unsafeMethods.has(init.method.toUpperCase()) && init.headers && !(idempotencyHeaderName in init.headers)) { init.headers = { ...init.headers, ...generateIdempotencyHeader() }; } // Attempt the request. for (let attempt = 0; ; ++attempt) { - const response = await originalFetch(input, init).then(response => + const response = await originalFetch(url, init).then(response => Object.assign(response, { idempotencyKey: init.headers && idempotencyHeaderName in init.headers ? (init.headers[idempotencyHeaderName] as string) : undefined }), ); // If this is the last attempt, return the response. From 43781ca7258672cf4e71a12aeeb04fc6a92cc37b Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Fri, 6 Sep 2024 15:26:38 +0200 Subject: [PATCH 17/23] Fix onboarding test. --- tests/unit/resources/onboarding.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/resources/onboarding.test.ts b/tests/unit/resources/onboarding.test.ts index 909f70d3..ab558a1e 100644 --- a/tests/unit/resources/onboarding.test.ts +++ b/tests/unit/resources/onboarding.test.ts @@ -49,7 +49,7 @@ test('get', () => { }); test('submit', () => { - new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { + return new NetworkMocker(getApiKeyClientProvider()).use(async ([mollieClient, networkMocker]) => { networkMocker.intercept('POST', '/onboarding/me', 204).twice(); const result = await bluster(mollieClient.onboarding.submit.bind(mollieClient.onboarding))(); From 64c61577e74b6a8459126feb4f179df80419c72d Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Tue, 10 Sep 2024 13:19:54 +0200 Subject: [PATCH 18/23] use nullish check instead, as per https://github.com/mollie/mollie-api-node/pull/358#discussion_r1751662837 --- src/communication/NetworkClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index f5830d8e..7899d46e 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -84,7 +84,7 @@ async function processFetchResponse(response: ResponseWithIdempotencyKey) { return body; } // Request was not successful, but the response body contains an error message. - if (body) { + if (null != body) { throw ApiError.createFromResponse(body, response.idempotencyKey); } // Request was not successful. From cae30e4da18ed3c901b3d98d03896d214c2ec193 Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Tue, 10 Sep 2024 13:20:59 +0200 Subject: [PATCH 19/23] improve docs, as per https://github.com/mollie/mollie-api-node/pull/358#discussion_r1751664232 --- src/communication/makeRetrying.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication/makeRetrying.ts b/src/communication/makeRetrying.ts index 6958fe70..2b5655c6 100644 --- a/src/communication/makeRetrying.ts +++ b/src/communication/makeRetrying.ts @@ -53,7 +53,7 @@ function parseRetryAfterHeader(response: fetch.Response): number | undefined { export type ResponseWithIdempotencyKey = Response & { idempotencyKey: string | undefined }; /** - * Wrapper around fetch, making it attempt requests multiple times in some scenarios. + * Returns a wrapped version of fetch, making it attempt requests multiple times in some scenarios. * * The idea is that if the Mollie API has a brief hiccup, the extra attempts may cause the request to succeed anyway. * From fe066d77bcdf099417d10fa179ac812525fede84 Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Tue, 10 Sep 2024 13:31:23 +0200 Subject: [PATCH 20/23] centralised error management, as per https://github.com/mollie/mollie-api-node/pull/358#discussion_r1751662211 --- src/communication/NetworkClient.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 7899d46e..a0ae19a0 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -98,7 +98,7 @@ interface Context {} * This class is essentially a wrapper around fetch. It simplifies communication with the Mollie API over the network. */ export default class NetworkClient { - protected readonly request: (pathname: string, options?: RequestInit) => Promise; + protected readonly request: (pathname: string, options?: RequestInit) => Promise; constructor({ apiKey, accessToken, @@ -130,7 +130,9 @@ export default class NetworkClient { // Create the request function. this.request = (pathname, options) => { const url = new URL(pathname, apiEndpoint); - return fetchWithRetries(url, { agent, ...options, headers: { ...headers, ...options?.headers } }); + return fetchWithRetries(url, { agent, ...options, headers: { ...headers, ...options?.headers } }) + .catch(throwApiError) + .then(processFetchResponse); }; } @@ -145,15 +147,15 @@ export default class NetworkClient { headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, body: JSON.stringify(body), }; - return this.request(buildUrl(pathname, query), config).catch(throwApiError).then(processFetchResponse); + return this.request(buildUrl(pathname, query), config); } async get(pathname: string, query?: SearchParameters): Promise { - return this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); + return this.request(buildUrl(pathname, query)); } async list(pathname: string, binderName: string, query?: SearchParameters): Promise { - const data = await this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); + const data = await this.request(buildUrl(pathname, query)); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded } = data; @@ -164,7 +166,7 @@ export default class NetworkClient { } async page(pathname: string, binderName: string, query?: SearchParameters): Promise, 'links' | 'count'>> { - const data = await this.request(buildUrl(pathname, query)).catch(throwApiError).then(processFetchResponse); + const data = await this.request(buildUrl(pathname, query)); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded, _links: links, count } = data; @@ -213,7 +215,7 @@ export default class NetworkClient { let url = buildUrl(pathname, { ...query, limit: popLimit() }); while (true) { // Request and parse the page from the Mollie API. - const data = await request(url).catch(throwApiError).then(processFetchResponse); + const data = await request(url); try { /* eslint-disable-next-line no-var */ var { _embedded: embedded, _links: links } = data; @@ -245,7 +247,7 @@ export default class NetworkClient { method: 'PATCH', body: JSON.stringify(data), }; - return this.request(buildUrl(pathname), config).catch(throwApiError).then(processFetchResponse); + return this.request(buildUrl(pathname), config); } async delete(pathname: string, context?: Context & IdempotencyParameter): Promise { @@ -256,6 +258,6 @@ export default class NetworkClient { headers: idempotencyKey ? { [idempotencyHeaderName]: idempotencyKey } : undefined, body: JSON.stringify(body), }; - return this.request(buildUrl(pathname), config).catch(throwApiError).then(processFetchResponse); + return this.request(buildUrl(pathname), config); } } From cf9ed419274e38df28a3dad9ba3859df41099cad Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Tue, 10 Sep 2024 13:40:32 +0200 Subject: [PATCH 21/23] improve request docs, as per https://github.com/mollie/mollie-api-node/pull/358#discussion_r1751661180 --- src/communication/NetworkClient.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index a0ae19a0..1bce5c44 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -98,6 +98,14 @@ interface Context {} * This class is essentially a wrapper around fetch. It simplifies communication with the Mollie API over the network. */ export default class NetworkClient { + /** + * Triggers a request to the Mollie API. + * + * In contrast to the underlying `fetch` function, this function will: + * - retry the request in some scenarios (see `retryingFetch`) + * - throw an `ApiError` if the response from the Mollie API indicates an error + * - appropriately process the response body before returning it (i.e. parsing it as JSON or throwing an ApiError if the response status indicates an error) + */ protected readonly request: (pathname: string, options?: RequestInit) => Promise; constructor({ apiKey, From a4568aa41a7e83b5e43d1090dee6cabcda83454d Mon Sep 17 00:00:00 2001 From: Jan Paepke Date: Tue, 10 Sep 2024 15:05:26 +0200 Subject: [PATCH 22/23] small fix to secretly support node 8 --- src/communication/NetworkClient.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 1bce5c44..16af661a 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -16,6 +16,7 @@ import breakUrl from './breakUrl'; import buildUrl, { type SearchParameters } from './buildUrl'; import dromedaryCase from './dromedaryCase'; import { idempotencyHeaderName, ResponseWithIdempotencyKey, retryingFetch } from './makeRetrying'; +import { URL } from 'url'; // This isn't necessary for newer node versions, but it allows us to secretly support node 8. Should we ever drop that completely and move to 10+, we can remove this import. /** * Like `[].map` but with support for non-array inputs, in which case this function behaves as if an array was passed From c3b6ce8d97c55779773aeacab87e64714dc72edb Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Tue, 10 Sep 2024 15:27:06 +0200 Subject: [PATCH 23/23] Update polyfill comments to reflect secret support. --- src/communication/NetworkClient.ts | 3 ++- src/communication/breakUrl.ts | 2 +- src/communication/buildUrl.ts | 2 +- src/plumbing/buildFromEntries.ts | 3 ++- src/plumbing/iteration/HelpfulIterator.ts | 3 --- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/communication/NetworkClient.ts b/src/communication/NetworkClient.ts index 16af661a..a88165c2 100644 --- a/src/communication/NetworkClient.ts +++ b/src/communication/NetworkClient.ts @@ -16,7 +16,8 @@ import breakUrl from './breakUrl'; import buildUrl, { type SearchParameters } from './buildUrl'; import dromedaryCase from './dromedaryCase'; import { idempotencyHeaderName, ResponseWithIdempotencyKey, retryingFetch } from './makeRetrying'; -import { URL } from 'url'; // This isn't necessary for newer node versions, but it allows us to secretly support node 8. Should we ever drop that completely and move to 10+, we can remove this import. +// The following line is only necessary for Node.js < 10.0.0, which we only secretly support. Should we ever drop that support completely, we can remove this import. +import { URL } from 'url'; /** * Like `[].map` but with support for non-array inputs, in which case this function behaves as if an array was passed diff --git a/src/communication/breakUrl.ts b/src/communication/breakUrl.ts index f559acea..c5d1870b 100644 --- a/src/communication/breakUrl.ts +++ b/src/communication/breakUrl.ts @@ -1,4 +1,4 @@ -// If support for Node.js < 10.0.0 is ever dropped, this import can be removed. +// The following line is only necessary for Node.js < 10.0.0, which we only secretly support. Should we ever drop that support completely, we can remove this import. import { URL } from 'url'; import buildFromEntries from '../plumbing/buildFromEntries'; diff --git a/src/communication/buildUrl.ts b/src/communication/buildUrl.ts index 2191ee58..3cb79354 100644 --- a/src/communication/buildUrl.ts +++ b/src/communication/buildUrl.ts @@ -1,4 +1,4 @@ -// If support for Node.js < 10.0.0 is ever dropped, this import can be removed. +// The following line is only necessary for Node.js < 10.0.0, which we only secretly support. Should we ever drop that support completely, we can remove this import. import { URLSearchParams } from 'url'; import { apply, runIf } from 'ruply'; diff --git a/src/plumbing/buildFromEntries.ts b/src/plumbing/buildFromEntries.ts index 85e78b0c..414187ec 100644 --- a/src/plumbing/buildFromEntries.ts +++ b/src/plumbing/buildFromEntries.ts @@ -1,7 +1,8 @@ /** * Returns an object generated from the passed a list of key-value pairs. * - * If support for Node.js < 12.0.0 is ever dropped, this function can be removed in favour of `Object.fromEntries`. + * This function only exists to support Node.js < 12.0.0, which we only secretly support. Should we ever drop that + * support completely, this function can be removed in favour of `Object.fromEntries`. */ export default ((): ((input: Iterable) => Record) => { if (Object.fromEntries != undefined) { diff --git a/src/plumbing/iteration/HelpfulIterator.ts b/src/plumbing/iteration/HelpfulIterator.ts index b5afc269..73d8bdce 100644 --- a/src/plumbing/iteration/HelpfulIterator.ts +++ b/src/plumbing/iteration/HelpfulIterator.ts @@ -66,9 +66,6 @@ export default class HelpfulIterator implements AsyncIterator this.next = wrappee.next.bind(wrappee); } - // Node.js < 10.0.0 does not support Symbol.asyncIterator. Symbol.asyncIterator therefore resolves to undefined, - // which actually creates a method called "undefined" callable as iterator.undefined(). Albeit odd, I don't feel this - // warrants a "fix", especially as it only affects old Node.js versions. [Symbol.asyncIterator]() { return this; }