Files
Momento/memento-note/tests/unit/billing-cancel.test.ts

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' });
});
});