test(backoffice): add unit tests for StripeService
- Add 8 test cases covering all StripeService methods - Mock Stripe SDK for isolated testing - Test createCustomer, createSubscription, cancelSubscription - Test listSubscriptions, createCheckoutSession, createBillingPortalSession - Test constructWebhookEvent and onModuleInit
This commit is contained in:
parent
cb31713307
commit
93367d7cd6
1 changed files with 159 additions and 0 deletions
159
backoffice/src/stripe/stripe.service.spec.ts
Normal file
159
backoffice/src/stripe/stripe.service.spec.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { StripeService } from './stripe.service';
|
||||
|
||||
// Mock Stripe
|
||||
jest.mock('stripe', () => {
|
||||
return jest.fn().mockImplementation(() => ({
|
||||
customers: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'cus_test123', email: 'test@example.com' }),
|
||||
},
|
||||
subscriptions: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'active' }),
|
||||
cancel: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'canceled' }),
|
||||
list: jest.fn().mockResolvedValue({ data: [] }),
|
||||
},
|
||||
checkout: {
|
||||
sessions: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'cs_test123', url: 'https://checkout.stripe.com/test' }),
|
||||
},
|
||||
},
|
||||
billingPortal: {
|
||||
sessions: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'bps_test123', url: 'https://billing.stripe.com/test' }),
|
||||
},
|
||||
},
|
||||
webhooks: {
|
||||
constructEvent: jest.fn().mockReturnValue({ type: 'checkout.session.completed' }),
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
describe('StripeService', () => {
|
||||
let service: StripeService;
|
||||
let configService: ConfigService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
StripeService,
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: {
|
||||
get: jest.fn((key: string) => {
|
||||
const config: Record<string, string> = {
|
||||
STRIPE_SECRET_KEY: 'sk_test_fake123',
|
||||
STRIPE_WEBHOOK_SECRET: 'whsec_test123',
|
||||
};
|
||||
return config[key];
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<StripeService>(StripeService);
|
||||
configService = module.get<ConfigService>(ConfigService);
|
||||
|
||||
// Initialize the service
|
||||
service.onModuleInit();
|
||||
});
|
||||
|
||||
describe('onModuleInit', () => {
|
||||
it('should initialize Stripe client when secret key is configured', () => {
|
||||
expect(service.getClient()).toBeDefined();
|
||||
});
|
||||
|
||||
it('should warn when STRIPE_SECRET_KEY is not configured', () => {
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||
jest.spyOn(configService, 'get').mockReturnValue(undefined);
|
||||
|
||||
const newService = new StripeService(configService);
|
||||
newService.onModuleInit();
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith('STRIPE_SECRET_KEY not configured');
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCustomer', () => {
|
||||
it('should create a customer with email and name', async () => {
|
||||
const result = await service.createCustomer('test@example.com', 'Test User');
|
||||
|
||||
expect(result).toEqual({ id: 'cus_test123', email: 'test@example.com' });
|
||||
expect(service.getClient().customers.create).toHaveBeenCalledWith({
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSubscription', () => {
|
||||
it('should create a subscription for a customer', async () => {
|
||||
const result = await service.createSubscription('cus_test123', 'price_test123');
|
||||
|
||||
expect(result).toEqual({ id: 'sub_test123', status: 'active' });
|
||||
expect(service.getClient().subscriptions.create).toHaveBeenCalledWith({
|
||||
customer: 'cus_test123',
|
||||
items: [{ price: 'price_test123' }],
|
||||
payment_behavior: 'default_incomplete',
|
||||
expand: ['latest_invoice.payment_intent'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelSubscription', () => {
|
||||
it('should cancel a subscription', async () => {
|
||||
const result = await service.cancelSubscription('sub_test123');
|
||||
|
||||
expect(result).toEqual({ id: 'sub_test123', status: 'canceled' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('listSubscriptions', () => {
|
||||
it('should list subscriptions for a customer', async () => {
|
||||
const result = await service.listSubscriptions('cus_test123');
|
||||
|
||||
expect(result).toEqual({ data: [] });
|
||||
expect(service.getClient().subscriptions.list).toHaveBeenCalledWith({
|
||||
customer: 'cus_test123',
|
||||
status: 'all',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCheckoutSession', () => {
|
||||
it('should create a checkout session', async () => {
|
||||
const result = await service.createCheckoutSession(
|
||||
'cus_test123',
|
||||
'price_test123',
|
||||
'https://example.com/success',
|
||||
'https://example.com/cancel',
|
||||
);
|
||||
|
||||
expect(result).toEqual({ id: 'cs_test123', url: 'https://checkout.stripe.com/test' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('createBillingPortalSession', () => {
|
||||
it('should create a billing portal session', async () => {
|
||||
const result = await service.createBillingPortalSession(
|
||||
'cus_test123',
|
||||
'https://example.com/return',
|
||||
);
|
||||
|
||||
expect(result).toEqual({ id: 'bps_test123', url: 'https://billing.stripe.com/test' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructWebhookEvent', () => {
|
||||
it('should construct a webhook event from request body', () => {
|
||||
const body = Buffer.from('{}');
|
||||
const signature = 'test_signature';
|
||||
|
||||
const result = service.constructWebhookEvent(body, signature);
|
||||
|
||||
expect(result).toEqual({ type: 'checkout.session.completed' });
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue