57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
import { ConfigService } from '@nestjs/config';
|
|
import { RawBodyRequest } from '@nestjs/common';
|
|
import { Request } from 'express';
|
|
import { SubscriptionService } from './subscription.service';
|
|
import { SubscriptionsController } from '../controllers/subscriptions.controller';
|
|
import { Subscription } from '@domain/entities/subscription.entity';
|
|
import { SubscriptionPlan } from '@domain/value-objects/subscription-plan.vo';
|
|
import { SubscriptionRepository } from '@domain/ports/out/subscription.repository';
|
|
import { LicenseRepository } from '@domain/ports/out/license.repository';
|
|
import { OrganizationRepository } from '@domain/ports/out/organization.repository';
|
|
import { UserRepository } from '@domain/ports/out/user.repository';
|
|
import { StripePort } from '@domain/ports/out/stripe.port';
|
|
|
|
describe('Stripe subscription deletion', () => {
|
|
it('persists cancellation with excess seats and accepts a duplicate event', async () => {
|
|
let saved = Subscription.create({
|
|
id: 'sub',
|
|
organizationId: 'org',
|
|
plan: SubscriptionPlan.gold(),
|
|
});
|
|
const save = jest.fn(async (value: Subscription) => {
|
|
saved = value;
|
|
});
|
|
const count = jest.fn(async () => 10);
|
|
const service = new SubscriptionService(
|
|
{ findByStripeSubscriptionId: async () => saved, save } as unknown as SubscriptionRepository,
|
|
{ countActiveBySubscriptionIdExcludingAdmins: count } as unknown as LicenseRepository,
|
|
{ findById: async () => null } as unknown as OrganizationRepository,
|
|
{} as UserRepository,
|
|
{
|
|
constructWebhookEvent: async () => ({
|
|
type: 'customer.subscription.deleted',
|
|
data: { object: { id: 'stripe-sub' } },
|
|
}),
|
|
} as unknown as StripePort,
|
|
new ConfigService()
|
|
);
|
|
await service.handleStripeWebhook(Buffer.from('signed fixture'), 'signature');
|
|
await service.handleStripeWebhook(Buffer.from('signed fixture'), 'signature');
|
|
expect(saved.plan.value).toBe('BRONZE');
|
|
expect(saved.status.value).toBe('CANCELED');
|
|
expect(save).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('does not acknowledge processing failures as successful delivery', async () => {
|
|
const handleStripeWebhook = jest.fn().mockRejectedValue(new Error('storage unavailable'));
|
|
const controller = new SubscriptionsController(
|
|
{ handleStripeWebhook } as unknown as SubscriptionService,
|
|
{} as OrganizationRepository
|
|
);
|
|
const req = { rawBody: Buffer.from('fixture') } as RawBodyRequest<Request>;
|
|
await expect(controller.handleWebhook('signature', req)).rejects.toMatchObject({ status: 500 });
|
|
handleStripeWebhook.mockResolvedValue(undefined);
|
|
await expect(controller.handleWebhook('signature', req)).resolves.toEqual({ received: true });
|
|
});
|
|
});
|