diff --git a/e2e/src/proformaInvoiceController/consolidateProformaInvoice.spec.ts b/e2e/src/proformaInvoiceController/consolidateProformaInvoice.spec.ts new file mode 100644 index 00000000..2f6b182f --- /dev/null +++ b/e2e/src/proformaInvoiceController/consolidateProformaInvoice.spec.ts @@ -0,0 +1,94 @@ +import { ProformaInvoicesController } from 'advanced-billing-sdk'; +import { createClient, createInvalidClient } from '../config'; +import { signUpSubscriptionGroup } from '../utils/subscriptionsGroups'; +import { waitForSeconds } from '../utils'; + +describe('Proforma Invoices Controller', () => { + let proformaInvoicesController: ProformaInvoicesController; + let invalidProformaInvoicesController: ProformaInvoicesController; + + beforeAll(async () => { + const client = createClient(); + const invalidClient = createInvalidClient(); + proformaInvoicesController = new ProformaInvoicesController(client); + invalidProformaInvoicesController = new ProformaInvoicesController( + invalidClient + ); + }); + + describe('Create Consolidate Proforma Invoice', () => { + test('should create consolidate proforma invoice with a success status code', async () => { + const { subscriptionGroupsSignedResponse } = + await signUpSubscriptionGroup(); + const uid = subscriptionGroupsSignedResponse.uid || ''; + const response = + await proformaInvoicesController.createConsolidatedProformaInvoice(uid); + expect(response.statusCode).toBe(201); + }); + + test('should throw 404 error when create consolidate proforma invoice with an invalid uid', async () => { + const promise = + proformaInvoicesController.createConsolidatedProformaInvoice('invalid'); + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(404); + }); + }); + + test('should throw 401 error when create consolidate proforma invoice with an invalid client', async () => { + const promise = + invalidProformaInvoicesController.createConsolidatedProformaInvoice( + 'invalid' + ); + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(401); + }); + }); + }); + + describe('List Subscription Group Proforma Invoices', () => { + test('should list subscription group proforma invoices already created for the subscription group', async () => { + const { subscriptionGroupsSignedResponse, customerResponse } = + await signUpSubscriptionGroup(); + const uid = subscriptionGroupsSignedResponse.uid || ''; + await proformaInvoicesController.createConsolidatedProformaInvoice(uid); + + // required to wait async creation on the service + await waitForSeconds(5); + + const listResponse = + await proformaInvoicesController.listSubscriptionGroupProformaInvoices( + uid + ); + + const [proformaInvoice] = listResponse.result.proformaInvoices || []; + expect(listResponse.statusCode).toBe(200); + expect(listResponse.result.proformaInvoices?.length).toBeGreaterThan(0); + expect(proformaInvoice.siteId).toBe(4511); + expect(proformaInvoice.customerId).toBe(customerResponse.customer.id); + }); + + test('should throw 404 when listSubscriptionGroupProformaInvoices with an invalid uid', async () => { + const promise = + proformaInvoicesController.listSubscriptionGroupProformaInvoices( + 'invalid' + ); + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(404); + }); + }); + + test('should throw 404 when listSubscriptionGroupProformaInvoices with an invalid client', async () => { + const promise = + invalidProformaInvoicesController.listSubscriptionGroupProformaInvoices( + 'invalid' + ); + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(401); + }); + }); + }); +}); diff --git a/e2e/src/proformaInvoiceController/previewSignUpProformaInvoice.spec.ts b/e2e/src/proformaInvoiceController/previewSignUpProformaInvoice.spec.ts new file mode 100644 index 00000000..61959dab --- /dev/null +++ b/e2e/src/proformaInvoiceController/previewSignUpProformaInvoice.spec.ts @@ -0,0 +1,134 @@ +import { ProformaInvoicesController } from 'advanced-billing-sdk'; +import { createClient, createInvalidClient } from '../config'; +import { uid } from 'uid'; +import createProduct from '../utils/products'; +import { createCustomer } from '../utils/customers'; + +describe('Create a Preview Sign Up Proforma Invoice', () => { + let proformaInvoicesController: ProformaInvoicesController; + let invalidProformaInvoicesController: ProformaInvoicesController; + + beforeAll(async () => { + const client = createClient(); + const invalidClient = createInvalidClient(); + proformaInvoicesController = new ProformaInvoicesController(client); + invalidProformaInvoicesController = new ProformaInvoicesController( + invalidClient + ); + }); + + test('should create a preview signup proforma invoice ', async () => { + const productHandle = uid(); + const { productResponse, productFamilyResponse } = await createProduct({ + productHandle, + }); + const customer = await createCustomer(); + + const response = + await proformaInvoicesController.previewSignupProformaInvoice(undefined, { + subscription: { + productHandle, + customerReference: uid(), + customerId: customer.id, + }, + }); + + const { currentProformaInvoice } = response.result.proformaInvoicePreview; + expect(response.statusCode).toBe(201); + expect(currentProformaInvoice?.customerId).toBe(customer.id); + + expect(currentProformaInvoice?.productName).toBe( + productResponse.product.name + ); + expect(currentProformaInvoice?.productFamilyName).toBe( + productFamilyResponse?.productFamily?.name + ); + }); + + test('should create a preview signup proforma invoice with next_proforma_invoice attribute', async () => { + const productHandle = uid(); + const { productResponse, productFamilyResponse } = await createProduct({ + productHandle, + }); + const customer = await createCustomer(); + + const response = + await proformaInvoicesController.previewSignupProformaInvoice( + 'nextProformaId', + { + subscription: { + productHandle, + customerReference: uid(), + customerId: customer.id, + }, + } + ); + + expect(response.request.url).toContain( + 'next_proforma_invoice=nextProformaId' + ); + const { currentProformaInvoice } = response.result.proformaInvoicePreview; + expect(response.statusCode).toBe(201); + expect(currentProformaInvoice?.customerId).toBe(customer.id); + + expect(currentProformaInvoice?.productName).toBe( + productResponse.product.name + ); + expect(currentProformaInvoice?.productFamilyName).toBe( + productFamilyResponse?.productFamily?.name + ); + }); + + test('should throw an error when user sends an invalid payload with a missing product', async () => { + const promise = proformaInvoicesController.previewSignupProformaInvoice( + '', + { + subscription: { + customerReference: uid(), + }, + } + ); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(422); + expect(reason.result.errors.base).toEqual(['Missing required product']); + }); + }); + + test('should throw an error when user sends an invalid payload with a missing customer', async () => { + const productHandle = uid(); + await createProduct({ + productHandle, + }); + const promise = proformaInvoicesController.previewSignupProformaInvoice( + '', + { + subscription: { + productHandle, + customerReference: uid(), + }, + } + ); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(422); + expect(reason.result.errors.customer).toEqual([ + 'Missing required customer attributes', + ]); + }); + }); + + test('should throw an 401 error with invalid credentials', async () => { + const promise = + invalidProformaInvoicesController.previewSignupProformaInvoice('', { + subscription: {}, + }); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(401); + }); + }); +}); diff --git a/e2e/src/proformaInvoiceController/signUpProformaInvoice.spec.ts b/e2e/src/proformaInvoiceController/signUpProformaInvoice.spec.ts new file mode 100644 index 00000000..c58b173f --- /dev/null +++ b/e2e/src/proformaInvoiceController/signUpProformaInvoice.spec.ts @@ -0,0 +1,114 @@ +import { ProformaInvoicesController } from 'advanced-billing-sdk'; +import { createClient, createInvalidClient } from '../config'; +import { uid } from 'uid'; +import createProduct from '../utils/products'; +import { createCustomer } from '../utils/customers'; + +describe('Create Sign up Proforma Invoice', () => { + let proformaInvoicesController: ProformaInvoicesController; + let invalidProformaInvoicesController: ProformaInvoicesController; + + beforeAll(async () => { + const client = createClient(); + const invalidClient = createInvalidClient(); + proformaInvoicesController = new ProformaInvoicesController(client); + invalidProformaInvoicesController = new ProformaInvoicesController( + invalidClient + ); + }); + + test('should create sign up proforma invoice when user sends a valid product and customer', async () => { + const productHandle = uid(); + const { productResponse, productFamilyResponse } = await createProduct({ + productHandle, + }); + const customer = await createCustomer(); + const response = + await proformaInvoicesController.createSignupProformaInvoice({ + subscription: { + productHandle: productHandle, + customerReference: uid(), + customerId: customer.id, + }, + }); + + expect(response.statusCode).toBe(201); + expect(response.result.customerId).toBe(customer.id); + expect(response.result.productName).toBe(productResponse.product.name); + expect(response.result.productFamilyName).toBe( + productFamilyResponse?.productFamily?.name + ); + }); + + test('should create sign up proforma invoice when user sends a valid product and customer attributes', async () => { + const productHandle = uid(); + const { productResponse, productFamilyResponse } = await createProduct({ + productHandle, + }); + const response = + await proformaInvoicesController.createSignupProformaInvoice({ + subscription: { + productHandle: productHandle, + customerReference: uid(), + customerAttributes: { + firstName: 'Martha', + lastName: 'Perez', + email: 'martha@example.com', + }, + }, + }); + + expect(response.statusCode).toBe(201); + expect(response.result.productName).toBe(productResponse.product.name); + expect(response.result.productFamilyName).toBe( + productFamilyResponse?.productFamily?.name + ); + }); + + test('should throw an error when user sends an invalid payload with a missing product', async () => { + const promise = proformaInvoicesController.createSignupProformaInvoice({ + subscription: { + customerReference: uid(), + }, + }); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(422); + expect(reason.result.errors.base).toEqual(['Missing required product']); + }); + }); + + test('should throw an error when user sends an invalid payload with a missing customer', async () => { + const productHandle = uid(); + await createProduct({ + productHandle, + }); + const promise = proformaInvoicesController.createSignupProformaInvoice({ + subscription: { + productHandle, + customerReference: uid(), + }, + }); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(422); + expect(reason.result.errors.customer).toEqual([ + 'Missing required customer attributes', + ]); + }); + }); + + test('should throw an 401 error with invalid credentials', async () => { + const promise = + invalidProformaInvoicesController.createSignupProformaInvoice({ + subscription: {}, + }); + + expect(promise).rejects.toThrow(); + await promise.catch((reason) => { + expect(reason.statusCode).toBe(401); + }); + }); +}); diff --git a/e2e/src/utils/components.ts b/e2e/src/utils/components.ts new file mode 100644 index 00000000..7a13a2d6 --- /dev/null +++ b/e2e/src/utils/components.ts @@ -0,0 +1,30 @@ +import { ComponentsController, PricingScheme } from 'advanced-billing-sdk'; +import { createClient } from '../config'; +import { uid } from 'uid'; + +export async function createComponent({ + productFamilyId, +}: { + productFamilyId: number; +}) { + const client = createClient(); + const componentsController = new ComponentsController(client); + const meteredComponent = { + productFamilyId, + name: 'test', + description: 'test', + quantity: 1, + unitName: 'test', + price: 1, + handle: uid(), + pricingScheme: PricingScheme.PerUnit, + unitPrice: 1, + }; + const response = await componentsController.createMeteredComponent( + productFamilyId, + { + meteredComponent, + } + ); + return response.result; +} diff --git a/e2e/src/utils/index.ts b/e2e/src/utils/index.ts index b9a97b65..b4335fe0 100644 --- a/e2e/src/utils/index.ts +++ b/e2e/src/utils/index.ts @@ -7,3 +7,11 @@ export async function cleanSite() { const response = await sitesController.clearSite(); return response; } + +export async function waitForSeconds(time: number) { + return new Promise((resolve) => + setTimeout(() => { + resolve('success'); + }, time * 1000) + ); +} diff --git a/e2e/src/utils/products.ts b/e2e/src/utils/products.ts new file mode 100644 index 00000000..567fcfaf --- /dev/null +++ b/e2e/src/utils/products.ts @@ -0,0 +1,41 @@ +import { + ProductFamiliesController, + ProductsController, +} from 'advanced-billing-sdk'; +import { createClient } from '../config'; +import { uid } from 'uid'; +import { product } from '../mocks/products'; + +export interface ProductConfig { + productFamilyName?: string; + productHandle?: string; + customerReference?: string; +} + +export default async function createProduct({ + productFamilyName = uid(), + productHandle = uid(), +}: ProductConfig) { + const client = createClient(); + const productFamiliesController = new ProductFamiliesController(client); + const productsController = new ProductsController(client); + const productFamilyResponse = ( + await productFamiliesController.createProductFamily({ + productFamily: { + name: productFamilyName, + description: 'generic product family', + }, + }) + ).result; + + const productResponse = ( + await productsController.createProduct( + productFamilyResponse.productFamily?.id || 0, + { + product: { ...product, handle: productHandle }, + } + ) + ).result; + + return { productResponse, productFamilyResponse }; +} diff --git a/e2e/src/utils/subscriptionsGroups.ts b/e2e/src/utils/subscriptionsGroups.ts new file mode 100644 index 00000000..3dea02a3 --- /dev/null +++ b/e2e/src/utils/subscriptionsGroups.ts @@ -0,0 +1,98 @@ +import { + CustomersController, + PaymentProfilesController, + PaymentType, + SubscriptionComponentsController, + SubscriptionGroupsController, +} from 'advanced-billing-sdk'; +import { createClient } from '../config'; + +import createProduct from './products'; +import { createComponent } from './components'; + +export async function signUpSubscriptionGroup() { + const client = createClient(); + const subscriptionGroupsController = new SubscriptionGroupsController(client); + const subscriptionsComponentsController = + new SubscriptionComponentsController(client); + + const { productResponse, productFamilyResponse } = await createProduct({}); + const product = productResponse.product; + const componentResponse = await createComponent({ + productFamilyId: productFamilyResponse?.productFamily?.id || 0, + }); + + const customersController = new CustomersController(client); + const paymentProfilesController = new PaymentProfilesController(client); + const body = { + customer: { + firstName: 'Martha', + lastName: 'Perez', + email: 'martha@example.com', + }, + }; + const customerResponse = (await customersController.createCustomer(body)) + .result; + + const { customer } = customerResponse; + + const { + result: { paymentProfile }, + } = await paymentProfilesController.createPaymentProfile({ + paymentProfile: { + customerId: customer.id, + bankName: 'Royal Bank of France', + bankAccountNumber: '0000001', + bankRoutingNumber: '0003', + bankBranchCode: '00006', + paymentType: PaymentType.BankAccount, + billingAddress: '20 Place de la Gare', + billingCity: 'Colombes', + billingState: 'Île-de-France', + billingZip: '92700', + billingCountry: 'FR', + }, + }); + + const subscriptionGroupsSignedResponse = ( + await subscriptionGroupsController.signupWithSubscriptionGroup({ + subscriptionGroup: { + payerId: customer.id, + paymentProfileId: paymentProfile.id, + + subscriptions: [ + { + components: [ + { + componentId: componentResponse?.component.id, + }, + ], + primary: true, + productHandle: product.handle || '', + couponCodes: [], + }, + ], + }, + }) + ).result; + + const subscriptionId = subscriptionGroupsSignedResponse.primarySubscriptionId; + const usageResponse = await subscriptionsComponentsController.createUsage( + subscriptionId || 0, + componentResponse?.component.id || 0, + { + usage: { + quantity: 1, + memo: 'memo test', + }, + } + ); + + return { + customerResponse, + subscriptionGroupsSignedResponse, + productResponse, + usageResponse, + componentResponse, + }; +}