From 9a545fab532f094e2d9b3417d5037e9dda2477e7 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Wed, 13 May 2026 15:29:27 +0200 Subject: [PATCH 1/2] fix: get prices correctly --- src/controller/payments.controller.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/controller/payments.controller.ts b/src/controller/payments.controller.ts index 406f938f..45f07d56 100644 --- a/src/controller/payments.controller.ts +++ b/src/controller/payments.controller.ts @@ -434,14 +434,16 @@ export function paymentsController( const prices = await stripePaymentsAdapter.getPrices(currencyValue); - const mappedPrices = prices.map((price) => ({ - id: price.id, - productId: price.productId, - currency: price.currency, - amount: price.amount, - bytes: price.bytes, - interval: price?.interval, - })); + const mappedPrices = prices + .filter((price) => price.type === UserType.Individual) + .map((price) => ({ + id: price.id, + productId: price.productId, + currency: price.currency, + amount: price.amount, + bytes: price.bytes, + interval: price.commitmentPlan ? 'year' : price?.interval, + })); return mappedPrices; }, From f776dd0d624c1d98fac123b7130c593bedc52b1b Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Wed, 13 May 2026 15:33:15 +0200 Subject: [PATCH 2/2] tests: add coverage --- .../controller/payments.controller.test.ts | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/tests/src/controller/payments.controller.test.ts b/tests/src/controller/payments.controller.test.ts index cc190f8b..fc60c2b6 100644 --- a/tests/src/controller/payments.controller.test.ts +++ b/tests/src/controller/payments.controller.test.ts @@ -24,6 +24,7 @@ import Stripe from 'stripe'; import { LicenseCodesService } from '../../../src/services/licenseCodes.service'; import { StripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; +import { UserType } from '../../../src/core/users/User'; jest.mock('../../../src/utils/assertUser'); jest.mock('../../../src/services/storage.service', () => { @@ -547,28 +548,76 @@ describe('Payment controller e2e tests', () => { }); describe('Get prices', () => { - test('When fetching the available prices, then they are returned with the necessary data', async () => { - const mockedPriceEntity = getPriceEntity(); + test('When no currency is provided, then EUR is used and prices are returned', async () => { + const mockedPriceEntity = getPriceEntity({ currency: 'eur' }); const expectedResponse = { id: mockedPriceEntity.id, + productId: mockedPriceEntity.productId, currency: mockedPriceEntity.currency, amount: mockedPriceEntity.amount, bytes: mockedPriceEntity.bytes, interval: mockedPriceEntity.interval, + }; + + const getPricesSpy = jest.spyOn(StripePaymentsAdapter.prototype, 'getPrices').mockResolvedValue([mockedPriceEntity]); + + const response = await app.inject({ method: 'GET', path: '/prices' }); + + expect(response.statusCode).toBe(200); + expect(getPricesSpy).toHaveBeenCalledWith('eur'); + expect(response.json()).toStrictEqual([expectedResponse]); + }); + + test('When a valid currency is provided, then prices are returned in that currency', async () => { + const mockedPriceEntity = getPriceEntity({ currency: 'usd' }); + const expectedResponse = { + id: mockedPriceEntity.id, productId: mockedPriceEntity.productId, + currency: mockedPriceEntity.currency, + amount: mockedPriceEntity.amount, + bytes: mockedPriceEntity.bytes, + interval: mockedPriceEntity.interval, }; - jest.spyOn(StripePaymentsAdapter.prototype, 'getPrices').mockResolvedValue([mockedPriceEntity]); + const getPricesSpy = jest.spyOn(StripePaymentsAdapter.prototype, 'getPrices').mockResolvedValue([mockedPriceEntity]); - const response = await app.inject({ - method: 'GET', - path: '/prices', - }); + const response = await app.inject({ method: 'GET', path: '/prices?currency=usd' }); + + expect(response.statusCode).toBe(200); + expect(getPricesSpy).toHaveBeenCalledWith('usd'); + expect(response.json()).toStrictEqual([expectedResponse]); + }); + + test('When an unsupported currency is provided, then a bad request error is returned', async () => { + const response = await app.inject({ method: 'GET', path: '/prices?currency=xyz' }); + + expect(response.statusCode).toBe(400); + expect(response.json()).toStrictEqual({ message: 'Bad request' }); + }); + + test('When there are business prices, then only individual prices are returned', async () => { + const individualPrice = getPriceEntity({ type: UserType.Individual }); + const businessPrice = getPriceEntity({ type: UserType.Business }); + jest.spyOn(StripePaymentsAdapter.prototype, 'getPrices').mockResolvedValue([individualPrice, businessPrice]); + + const response = await app.inject({ method: 'GET', path: '/prices' }); const responseBody = response.json(); expect(response.statusCode).toBe(200); - expect(responseBody).toStrictEqual([expectedResponse]); + expect(responseBody).toHaveLength(1); + expect(responseBody[0].id).toBe(individualPrice.id); + }); + + test('When a price has an annual commitment, then the interval is returned as year', async () => { + const mockedPriceEntity = getPriceEntity({ commitmentPlan: true, interval: 'month' }); + + jest.spyOn(StripePaymentsAdapter.prototype, 'getPrices').mockResolvedValue([mockedPriceEntity]); + + const response = await app.inject({ method: 'GET', path: '/prices' }); + + expect(response.statusCode).toBe(200); + expect(response.json()[0].interval).toBe('year'); }); }); });