backend unit tests
This commit is contained in:
805
backend/tests/unit/routes/stripe.test.js
Normal file
805
backend/tests/unit/routes/stripe.test.js
Normal file
@@ -0,0 +1,805 @@
|
||||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('jsonwebtoken');
|
||||
jest.mock('../../../models', () => ({
|
||||
User: {
|
||||
findByPk: jest.fn(),
|
||||
create: jest.fn(),
|
||||
findOne: jest.fn()
|
||||
},
|
||||
Item: {}
|
||||
}));
|
||||
|
||||
jest.mock('../../../services/stripeService', () => ({
|
||||
getCheckoutSession: jest.fn(),
|
||||
createConnectedAccount: jest.fn(),
|
||||
createAccountLink: jest.fn(),
|
||||
getAccountStatus: jest.fn(),
|
||||
createCustomer: jest.fn(),
|
||||
createSetupCheckoutSession: jest.fn()
|
||||
}));
|
||||
|
||||
// Mock auth middleware
|
||||
jest.mock('../../../middleware/auth', () => ({
|
||||
authenticateToken: (req, res, next) => {
|
||||
// Mock authenticated user
|
||||
if (req.headers.authorization) {
|
||||
req.user = { id: 1 };
|
||||
next();
|
||||
} else {
|
||||
res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const { User } = require('../../../models');
|
||||
const StripeService = require('../../../services/stripeService');
|
||||
const stripeRoutes = require('../../../routes/stripe');
|
||||
|
||||
// Set up Express app for testing
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use('/stripe', stripeRoutes);
|
||||
|
||||
describe('Stripe Routes', () => {
|
||||
let consoleSpy, consoleErrorSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Set up console spies
|
||||
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
consoleSpy.mockRestore();
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
|
||||
describe('GET /checkout-session/:sessionId', () => {
|
||||
it('should retrieve checkout session successfully', async () => {
|
||||
const mockSession = {
|
||||
status: 'complete',
|
||||
payment_status: 'paid',
|
||||
customer_details: {
|
||||
email: 'test@example.com'
|
||||
},
|
||||
setup_intent: {
|
||||
id: 'seti_123456789',
|
||||
status: 'succeeded'
|
||||
},
|
||||
metadata: {
|
||||
userId: '1'
|
||||
}
|
||||
};
|
||||
|
||||
StripeService.getCheckoutSession.mockResolvedValue(mockSession);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/checkout-session/cs_123456789');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
status: 'complete',
|
||||
payment_status: 'paid',
|
||||
customer_email: 'test@example.com',
|
||||
setup_intent: {
|
||||
id: 'seti_123456789',
|
||||
status: 'succeeded'
|
||||
},
|
||||
metadata: {
|
||||
userId: '1'
|
||||
}
|
||||
});
|
||||
|
||||
expect(StripeService.getCheckoutSession).toHaveBeenCalledWith('cs_123456789');
|
||||
});
|
||||
|
||||
it('should handle missing customer_details gracefully', async () => {
|
||||
const mockSession = {
|
||||
status: 'complete',
|
||||
payment_status: 'paid',
|
||||
customer_details: null,
|
||||
setup_intent: null,
|
||||
metadata: {}
|
||||
};
|
||||
|
||||
StripeService.getCheckoutSession.mockResolvedValue(mockSession);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/checkout-session/cs_123456789');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
status: 'complete',
|
||||
payment_status: 'paid',
|
||||
customer_email: undefined,
|
||||
setup_intent: null,
|
||||
metadata: {}
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle checkout session retrieval errors', async () => {
|
||||
const error = new Error('Session not found');
|
||||
StripeService.getCheckoutSession.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/checkout-session/invalid_session');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Session not found' });
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
'Error retrieving checkout session:',
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle missing session ID', async () => {
|
||||
const error = new Error('Invalid session ID');
|
||||
StripeService.getCheckoutSession.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/checkout-session/');
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /accounts', () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
stripeConnectedAccountId: null,
|
||||
update: jest.fn()
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockUser.update.mockReset();
|
||||
mockUser.stripeConnectedAccountId = null;
|
||||
});
|
||||
|
||||
it('should create connected account successfully', async () => {
|
||||
const mockAccount = {
|
||||
id: 'acct_123456789',
|
||||
email: 'test@example.com',
|
||||
country: 'US'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createConnectedAccount.mockResolvedValue(mockAccount);
|
||||
mockUser.update.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
stripeConnectedAccountId: 'acct_123456789',
|
||||
success: true
|
||||
});
|
||||
|
||||
expect(User.findByPk).toHaveBeenCalledWith(1);
|
||||
expect(StripeService.createConnectedAccount).toHaveBeenCalledWith({
|
||||
email: 'test@example.com',
|
||||
country: 'US'
|
||||
});
|
||||
expect(mockUser.update).toHaveBeenCalledWith({
|
||||
stripeConnectedAccountId: 'acct_123456789'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error if user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.body).toEqual({ error: 'User not found' });
|
||||
expect(StripeService.createConnectedAccount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return error if user already has connected account', async () => {
|
||||
const userWithAccount = {
|
||||
...mockUser,
|
||||
stripeConnectedAccountId: 'acct_existing'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(userWithAccount);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'User already has a connected account' });
|
||||
expect(StripeService.createConnectedAccount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts');
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toEqual({ error: 'No token provided' });
|
||||
});
|
||||
|
||||
it('should handle Stripe account creation errors', async () => {
|
||||
const error = new Error('Invalid email address');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createConnectedAccount.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Invalid email address' });
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
'Error creating connected account:',
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle database update errors', async () => {
|
||||
const mockAccount = { id: 'acct_123456789' };
|
||||
const dbError = new Error('Database update failed');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createConnectedAccount.mockResolvedValue(mockAccount);
|
||||
mockUser.update.mockRejectedValue(dbError);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Database update failed' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /account-links', () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
stripeConnectedAccountId: 'acct_123456789'
|
||||
};
|
||||
|
||||
it('should create account link successfully', async () => {
|
||||
const mockAccountLink = {
|
||||
url: 'https://connect.stripe.com/setup/e/acct_123456789',
|
||||
expires_at: Date.now() + 3600
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createAccountLink.mockResolvedValue(mockAccountLink);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh',
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
url: mockAccountLink.url,
|
||||
expiresAt: mockAccountLink.expires_at
|
||||
});
|
||||
|
||||
expect(StripeService.createAccountLink).toHaveBeenCalledWith(
|
||||
'acct_123456789',
|
||||
'http://localhost:3000/refresh',
|
||||
'http://localhost:3000/return'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return error if no connected account found', async () => {
|
||||
const userWithoutAccount = {
|
||||
id: 1,
|
||||
stripeConnectedAccountId: null
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(userWithoutAccount);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh',
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'No connected account found' });
|
||||
expect(StripeService.createAccountLink).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return error if user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh',
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'No connected account found' });
|
||||
});
|
||||
|
||||
it('should validate required URLs', async () => {
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh'
|
||||
// Missing returnUrl
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'refreshUrl and returnUrl are required' });
|
||||
expect(StripeService.createAccountLink).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should validate both URLs are provided', async () => {
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
// Missing refreshUrl
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'refreshUrl and returnUrl are required' });
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh',
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should handle Stripe account link creation errors', async () => {
|
||||
const error = new Error('Account not found');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createAccountLink.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/account-links')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({
|
||||
refreshUrl: 'http://localhost:3000/refresh',
|
||||
returnUrl: 'http://localhost:3000/return'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Account not found' });
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
'Error creating account link:',
|
||||
error
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /account-status', () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
stripeConnectedAccountId: 'acct_123456789'
|
||||
};
|
||||
|
||||
it('should get account status successfully', async () => {
|
||||
const mockAccountStatus = {
|
||||
id: 'acct_123456789',
|
||||
details_submitted: true,
|
||||
payouts_enabled: true,
|
||||
capabilities: {
|
||||
transfers: { status: 'active' }
|
||||
},
|
||||
requirements: {
|
||||
pending_verification: [],
|
||||
currently_due: [],
|
||||
past_due: []
|
||||
}
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.getAccountStatus.mockResolvedValue(mockAccountStatus);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/account-status')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
accountId: 'acct_123456789',
|
||||
detailsSubmitted: true,
|
||||
payoutsEnabled: true,
|
||||
capabilities: {
|
||||
transfers: { status: 'active' }
|
||||
},
|
||||
requirements: {
|
||||
pending_verification: [],
|
||||
currently_due: [],
|
||||
past_due: []
|
||||
}
|
||||
});
|
||||
|
||||
expect(StripeService.getAccountStatus).toHaveBeenCalledWith('acct_123456789');
|
||||
});
|
||||
|
||||
it('should return error if no connected account found', async () => {
|
||||
const userWithoutAccount = {
|
||||
id: 1,
|
||||
stripeConnectedAccountId: null
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(userWithoutAccount);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/account-status')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'No connected account found' });
|
||||
expect(StripeService.getAccountStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return error if user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/account-status')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toEqual({ error: 'No connected account found' });
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await request(app)
|
||||
.get('/stripe/account-status');
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should handle Stripe account status retrieval errors', async () => {
|
||||
const error = new Error('Account not found');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.getAccountStatus.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/stripe/account-status')
|
||||
.set('Authorization', 'Bearer valid_token');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Account not found' });
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
'Error getting account status:',
|
||||
error
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /create-setup-checkout-session', () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
stripeCustomerId: null,
|
||||
update: jest.fn()
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockUser.update.mockReset();
|
||||
mockUser.stripeCustomerId = null;
|
||||
});
|
||||
|
||||
it('should create setup checkout session for new customer', async () => {
|
||||
const mockCustomer = {
|
||||
id: 'cus_123456789',
|
||||
email: 'test@example.com'
|
||||
};
|
||||
|
||||
const mockSession = {
|
||||
id: 'cs_123456789',
|
||||
client_secret: 'cs_123456789_secret_test'
|
||||
};
|
||||
|
||||
const rentalData = {
|
||||
itemId: '123',
|
||||
startDate: '2023-12-01',
|
||||
endDate: '2023-12-03'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockResolvedValue(mockCustomer);
|
||||
StripeService.createSetupCheckoutSession.mockResolvedValue(mockSession);
|
||||
mockUser.update.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({ rentalData });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
clientSecret: 'cs_123456789_secret_test',
|
||||
sessionId: 'cs_123456789'
|
||||
});
|
||||
|
||||
expect(User.findByPk).toHaveBeenCalledWith(1);
|
||||
expect(StripeService.createCustomer).toHaveBeenCalledWith({
|
||||
email: 'test@example.com',
|
||||
name: 'John Doe',
|
||||
metadata: {
|
||||
userId: '1'
|
||||
}
|
||||
});
|
||||
expect(mockUser.update).toHaveBeenCalledWith({ stripeCustomerId: 'cus_123456789' });
|
||||
expect(StripeService.createSetupCheckoutSession).toHaveBeenCalledWith({
|
||||
customerId: 'cus_123456789',
|
||||
metadata: {
|
||||
rentalData: JSON.stringify(rentalData)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should use existing customer ID if available', async () => {
|
||||
const userWithCustomer = {
|
||||
...mockUser,
|
||||
stripeCustomerId: 'cus_existing123'
|
||||
};
|
||||
|
||||
const mockSession = {
|
||||
id: 'cs_123456789',
|
||||
client_secret: 'cs_123456789_secret_test'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(userWithCustomer);
|
||||
StripeService.createSetupCheckoutSession.mockResolvedValue(mockSession);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
clientSecret: 'cs_123456789_secret_test',
|
||||
sessionId: 'cs_123456789'
|
||||
});
|
||||
|
||||
expect(StripeService.createCustomer).not.toHaveBeenCalled();
|
||||
expect(userWithCustomer.update).not.toHaveBeenCalled();
|
||||
expect(StripeService.createSetupCheckoutSession).toHaveBeenCalledWith({
|
||||
customerId: 'cus_existing123',
|
||||
metadata: {}
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle session without rental data', async () => {
|
||||
const mockCustomer = {
|
||||
id: 'cus_123456789'
|
||||
};
|
||||
|
||||
const mockSession = {
|
||||
id: 'cs_123456789',
|
||||
client_secret: 'cs_123456789_secret_test'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockResolvedValue(mockCustomer);
|
||||
StripeService.createSetupCheckoutSession.mockResolvedValue(mockSession);
|
||||
mockUser.update.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(StripeService.createSetupCheckoutSession).toHaveBeenCalledWith({
|
||||
customerId: 'cus_123456789',
|
||||
metadata: {}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error if user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.body).toEqual({ error: 'User not found' });
|
||||
expect(StripeService.createCustomer).not.toHaveBeenCalled();
|
||||
expect(StripeService.createSetupCheckoutSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should handle customer creation errors', async () => {
|
||||
const error = new Error('Invalid email address');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Invalid email address' });
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
'Error creating setup checkout session:',
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle database update errors', async () => {
|
||||
const mockCustomer = { id: 'cus_123456789' };
|
||||
const dbError = new Error('Database update failed');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockResolvedValue(mockCustomer);
|
||||
mockUser.update.mockRejectedValue(dbError);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Database update failed' });
|
||||
});
|
||||
|
||||
it('should handle session creation errors', async () => {
|
||||
const mockCustomer = { id: 'cus_123456789' };
|
||||
const sessionError = new Error('Session creation failed');
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockResolvedValue(mockCustomer);
|
||||
mockUser.update.mockResolvedValue(mockUser);
|
||||
StripeService.createSetupCheckoutSession.mockRejectedValue(sessionError);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Session creation failed' });
|
||||
});
|
||||
|
||||
it('should handle complex rental data', async () => {
|
||||
const mockCustomer = { id: 'cus_123456789' };
|
||||
const mockSession = {
|
||||
id: 'cs_123456789',
|
||||
client_secret: 'cs_123456789_secret_test'
|
||||
};
|
||||
|
||||
const complexRentalData = {
|
||||
itemId: '123',
|
||||
startDate: '2023-12-01',
|
||||
endDate: '2023-12-03',
|
||||
totalAmount: 150.00,
|
||||
additionalServices: ['cleaning', 'delivery'],
|
||||
notes: 'Special instructions'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createCustomer.mockResolvedValue(mockCustomer);
|
||||
StripeService.createSetupCheckoutSession.mockResolvedValue(mockSession);
|
||||
mockUser.update.mockResolvedValue(mockUser);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.send({ rentalData: complexRentalData });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(StripeService.createSetupCheckoutSession).toHaveBeenCalledWith({
|
||||
customerId: 'cus_123456789',
|
||||
metadata: {
|
||||
rentalData: JSON.stringify(complexRentalData)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error handling and edge cases', () => {
|
||||
it('should handle malformed JSON in rental data', async () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
stripeCustomerId: 'cus_123456789'
|
||||
};
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
|
||||
// This should work fine as Express will parse valid JSON
|
||||
const response = await request(app)
|
||||
.post('/stripe/create-setup-checkout-session')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"rentalData":{"itemId":"123"}}');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should handle very large session IDs', async () => {
|
||||
const longSessionId = 'cs_' + 'a'.repeat(100);
|
||||
const error = new Error('Session ID too long');
|
||||
|
||||
StripeService.getCheckoutSession.mockRejectedValue(error);
|
||||
|
||||
const response = await request(app)
|
||||
.get(`/stripe/checkout-session/${longSessionId}`);
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body).toEqual({ error: 'Session ID too long' });
|
||||
});
|
||||
|
||||
it('should handle concurrent requests for same user', async () => {
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
stripeConnectedAccountId: null,
|
||||
update: jest.fn().mockResolvedValue({})
|
||||
};
|
||||
|
||||
const mockAccount = { id: 'acct_123456789' };
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
StripeService.createConnectedAccount.mockResolvedValue(mockAccount);
|
||||
|
||||
// Simulate concurrent requests
|
||||
const [response1, response2] = await Promise.all([
|
||||
request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token'),
|
||||
request(app)
|
||||
.post('/stripe/accounts')
|
||||
.set('Authorization', 'Bearer valid_token')
|
||||
]);
|
||||
|
||||
// Both should succeed (in this test scenario)
|
||||
expect(response1.status).toBe(200);
|
||||
expect(response2.status).toBe(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user