137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
// Mock the dependencies before importing the function
|
|
vi.mock('@/lib/prisma', () => ({
|
|
prisma: {
|
|
subscription: {
|
|
findUnique: vi.fn(),
|
|
update: vi.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/lib/stripe', () => ({
|
|
stripe: {
|
|
subscriptions: {
|
|
update: vi.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
import { cancelSubscription } from '@/lib/billing/cancel-subscription';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { stripe } from '@/lib/stripe';
|
|
|
|
describe('cancelSubscription', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('should return error when userId is missing', async () => {
|
|
const result = await cancelSubscription('');
|
|
expect(result).toEqual({ success: false, error: 'User ID is required' });
|
|
expect(prisma.subscription.findUnique).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return error when no subscription is found in db', async () => {
|
|
vi.mocked(prisma.subscription.findUnique).mockResolvedValue(null);
|
|
|
|
const result = await cancelSubscription('user_123');
|
|
expect(result).toEqual({ success: false, error: 'No active subscription found' });
|
|
expect(prisma.subscription.findUnique).toHaveBeenCalledWith({
|
|
where: { userId: 'user_123' },
|
|
});
|
|
expect(stripe.subscriptions.update).not.toHaveBeenCalled();
|
|
expect(prisma.subscription.update).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should cancel subscription in Stripe and update DB successfully', async () => {
|
|
const mockDbSub = {
|
|
userId: 'user_123',
|
|
stripeSubscriptionId: 'sub_stripe_123',
|
|
currentPeriodEnd: new Date(1702678400 * 1000),
|
|
};
|
|
const mockStripeSub = {
|
|
id: 'sub_stripe_123',
|
|
canceled_at: 1700100000,
|
|
current_period_end: 1702678400,
|
|
};
|
|
|
|
vi.mocked(prisma.subscription.findUnique).mockResolvedValue(mockDbSub as any);
|
|
vi.mocked(stripe.subscriptions.update).mockResolvedValue(mockStripeSub as any);
|
|
vi.mocked(prisma.subscription.update).mockResolvedValue({} as any);
|
|
|
|
const result = await cancelSubscription('user_123');
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(stripe.subscriptions.update).toHaveBeenCalledWith('sub_stripe_123', {
|
|
cancel_at_period_end: true,
|
|
});
|
|
expect(prisma.subscription.update).toHaveBeenCalledWith({
|
|
where: { userId: 'user_123' },
|
|
data: expect.objectContaining({
|
|
cancelAtPeriodEnd: true,
|
|
canceledAt: expect.any(Date),
|
|
currentPeriodEnd: expect.any(Date),
|
|
}),
|
|
});
|
|
});
|
|
|
|
it('should support local / mock mode cancel when stripeSubscriptionId is missing', async () => {
|
|
const mockDbSub = {
|
|
userId: 'user_123',
|
|
stripeSubscriptionId: null,
|
|
};
|
|
|
|
vi.mocked(prisma.subscription.findUnique).mockResolvedValue(mockDbSub as any);
|
|
vi.mocked(prisma.subscription.update).mockResolvedValue({} as any);
|
|
|
|
const result = await cancelSubscription('user_123');
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(stripe.subscriptions.update).not.toHaveBeenCalled();
|
|
expect(prisma.subscription.update).toHaveBeenCalledWith({
|
|
where: { userId: 'user_123' },
|
|
data: expect.objectContaining({
|
|
cancelAtPeriodEnd: true,
|
|
canceledAt: expect.any(Date),
|
|
}),
|
|
});
|
|
});
|
|
|
|
it('should return error when Stripe API call fails', async () => {
|
|
const mockDbSub = {
|
|
userId: 'user_123',
|
|
stripeSubscriptionId: 'sub_stripe_123',
|
|
};
|
|
|
|
vi.mocked(prisma.subscription.findUnique).mockResolvedValue(mockDbSub as any);
|
|
vi.mocked(stripe.subscriptions.update).mockRejectedValue(new Error('Stripe connection error'));
|
|
|
|
const result = await cancelSubscription('user_123');
|
|
|
|
expect(result).toEqual({ success: false, error: 'Stripe connection error' });
|
|
expect(prisma.subscription.update).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return error when database write fails', async () => {
|
|
const mockDbSub = {
|
|
userId: 'user_123',
|
|
stripeSubscriptionId: 'sub_stripe_123',
|
|
};
|
|
const mockStripeSub = {
|
|
id: 'sub_stripe_123',
|
|
canceled_at: 1700100000,
|
|
current_period_end: 1702678400,
|
|
};
|
|
|
|
vi.mocked(prisma.subscription.findUnique).mockResolvedValue(mockDbSub as any);
|
|
vi.mocked(stripe.subscriptions.update).mockResolvedValue(mockStripeSub as any);
|
|
vi.mocked(prisma.subscription.update).mockRejectedValue(new Error('Prisma database error'));
|
|
|
|
const result = await cancelSubscription('user_123');
|
|
|
|
expect(result).toEqual({ success: false, error: 'Prisma database error' });
|
|
});
|
|
});
|