Bläddra i källkod

fix: allow to cancel previous subscription when activating premium features in e2e tests

Karol Sójko 1 år sedan
förälder
incheckning
15af5635f0

+ 2 - 0
packages/auth/src/Bootstrap/AuthServiceInterface.ts

@@ -3,7 +3,9 @@ import { Result, ServiceInterface } from '@standardnotes/domain-core'
 export interface AuthServiceInterface extends ServiceInterface {
   activatePremiumFeatures(dto: {
     username: string
+    subscriptionId: number
     subscriptionPlanName?: string
     endsAt?: Date
+    cancelPreviousSubscription?: boolean
   }): Promise<Result<string>>
 }

+ 2 - 0
packages/auth/src/Bootstrap/Service.ts

@@ -26,9 +26,11 @@ export class Service implements AuthServiceInterface {
 
   async activatePremiumFeatures(dto: {
     username: string
+    subscriptionId: number
     subscriptionPlanName?: string
     uploadBytesLimit?: number
     endsAt?: Date
+    cancelPreviousSubscription?: boolean
   }): Promise<Result<string>> {
     if (!this.container) {
       return Result.fail('Container not initialized')

+ 24 - 3
packages/auth/src/Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures.spec.ts

@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
 import { ActivatePremiumFeatures } from './ActivatePremiumFeatures'
 import { User } from '../../User/User'
 import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
+import { UserSubscription } from '../../Subscription/UserSubscription'
 
 describe('ActivatePremiumFeatures', () => {
   let userRepository: UserRepositoryInterface
@@ -31,6 +32,7 @@ describe('ActivatePremiumFeatures', () => {
     userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(user)
 
     userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
+    userSubscriptionRepository.findOneByUserUuid = jest.fn().mockResolvedValue(null)
     userSubscriptionRepository.save = jest.fn()
 
     roleService = {} as jest.Mocked<RoleServiceInterface>
@@ -48,7 +50,7 @@ describe('ActivatePremiumFeatures', () => {
   it('should return error when username is invalid', async () => {
     const useCase = createUseCase()
 
-    const result = await useCase.execute({ username: '' })
+    const result = await useCase.execute({ username: '', subscriptionId: 1 })
 
     expect(result.isFailed()).toBe(true)
     expect(result.getError()).toBe('Username cannot be empty')
@@ -59,7 +61,7 @@ describe('ActivatePremiumFeatures', () => {
 
     const useCase = createUseCase()
 
-    const result = await useCase.execute({ username: 'test@test.te' })
+    const result = await useCase.execute({ username: 'test@test.te', subscriptionId: 1 })
 
     expect(result.isFailed()).toBe(true)
     expect(result.getError()).toBe('User not found with username: test@test.te')
@@ -68,7 +70,24 @@ describe('ActivatePremiumFeatures', () => {
   it('should save a subscription and add role to user', async () => {
     const useCase = createUseCase()
 
-    const result = await useCase.execute({ username: 'test@test.te' })
+    const result = await useCase.execute({ username: 'test@test.te', subscriptionId: 1 })
+
+    expect(result.isFailed()).toBe(false)
+
+    expect(userSubscriptionRepository.save).toHaveBeenCalled()
+    expect(roleService.addUserRoleBasedOnSubscription).toHaveBeenCalled()
+  })
+
+  it('should cancel previous subscription if cancelPreviousSubscription is true', async () => {
+    userSubscriptionRepository.findOneByUserUuid = jest.fn().mockResolvedValue({} as jest.Mocked<UserSubscription>)
+
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      username: 'test@test.te',
+      subscriptionId: 1,
+      cancelPreviousSubscription: true,
+    })
 
     expect(result.isFailed()).toBe(false)
 
@@ -81,6 +100,7 @@ describe('ActivatePremiumFeatures', () => {
 
     const result = await useCase.execute({
       username: 'test@test.te',
+      subscriptionId: 1,
       subscriptionPlanName: 'PRO_PLAN',
       endsAt: new Date('2024-01-01T00:00:00.000Z'),
     })
@@ -93,6 +113,7 @@ describe('ActivatePremiumFeatures', () => {
 
     const result = await useCase.execute({
       username: 'test@test.te',
+      subscriptionId: 1,
       subscriptionPlanName: 'some invalid plan name',
       endsAt: new Date('2024-01-01T00:00:00.000Z'),
     })

+ 12 - 1
packages/auth/src/Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures.ts

@@ -30,6 +30,17 @@ export class ActivatePremiumFeatures implements UseCaseInterface<string> {
     if (user === null) {
       return Result.fail(`User not found with username: ${username.value}`)
     }
+
+    if (dto.cancelPreviousSubscription) {
+      const previousSubscription = await this.userSubscriptionRepository.findOneByUserUuid(user.uuid)
+      if (previousSubscription) {
+        previousSubscription.cancelled = true
+        previousSubscription.updatedAt = this.timer.getTimestampInMicroseconds()
+
+        await this.userSubscriptionRepository.save(previousSubscription)
+      }
+    }
+
     const subscriptionPlanNameString = dto.subscriptionPlanName ?? SubscriptionPlanName.NAMES.ProPlan
     const subscriptionPlanNameOrError = SubscriptionPlanName.create(subscriptionPlanNameString)
     if (subscriptionPlanNameOrError.isFailed()) {
@@ -48,7 +59,7 @@ export class ActivatePremiumFeatures implements UseCaseInterface<string> {
     subscription.updatedAt = timestamp
     subscription.endsAt = this.timer.convertDateToMicroseconds(endsAt)
     subscription.cancelled = false
-    subscription.subscriptionId = 1
+    subscription.subscriptionId = dto.subscriptionId
     subscription.subscriptionType = UserSubscriptionType.Regular
 
     await this.userSubscriptionRepository.save(subscription)

+ 2 - 0
packages/auth/src/Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeaturesDTO.ts

@@ -1,6 +1,8 @@
 export interface ActivatePremiumFeaturesDTO {
   username: string
+  subscriptionId: number
   subscriptionPlanName?: string
   uploadBytesLimit?: number
   endsAt?: Date
+  cancelPreviousSubscription?: boolean
 }

+ 2 - 9
packages/auth/src/Infra/TypeORM/TypeORMSubscriptionSettingRepository.ts

@@ -38,20 +38,13 @@ export class TypeORMSubscriptionSettingRepository implements SubscriptionSetting
     name: string,
     userSubscriptionUuid: string,
   ): Promise<SubscriptionSetting | null> {
-    const settings = await this.ormRepository
+    return this.ormRepository
       .createQueryBuilder('setting')
       .where('setting.name = :name AND setting.user_subscription_uuid = :userSubscriptionUuid', {
         name,
         userSubscriptionUuid,
       })
       .orderBy('updated_at', 'DESC')
-      .limit(1)
-      .getMany()
-
-    if (settings.length === 0) {
-      return null
-    }
-
-    return settings.pop() as SubscriptionSetting
+      .getOne()
   }
 }

+ 4 - 0
packages/home-server/src/Server/HomeServer.ts

@@ -141,9 +141,11 @@ export class HomeServer implements HomeServerInterface {
           app.post('/e2e/activate-premium', (request: Request, response: Response) => {
             void this.activatePremiumFeatures({
               username: request.body.username,
+              subscriptionId: request.body.subscriptionId,
               subscriptionPlanName: request.body.subscriptionPlanName,
               uploadBytesLimit: request.body.uploadBytesLimit,
               endsAt: request.body.endsAt ? new Date(request.body.endsAt) : undefined,
+              cancelPreviousSubscription: request.body.cancelPreviousSubscription,
             }).then((result) => {
               if (result.isFailed()) {
                 response.status(400).send({ error: { message: result.getError() } })
@@ -219,9 +221,11 @@ export class HomeServer implements HomeServerInterface {
 
   async activatePremiumFeatures(dto: {
     username: string
+    subscriptionId: number
     subscriptionPlanName?: string
     uploadBytesLimit?: number
     endsAt?: Date
+    cancelPreviousSubscription?: boolean
   }): Promise<Result<string>> {
     if (!this.isRunning() || !this.authService) {
       return Result.fail('Home server is not running.')

+ 2 - 0
packages/home-server/src/Server/HomeServerInterface.ts

@@ -5,9 +5,11 @@ export interface HomeServerInterface {
   start(configuration?: HomeServerConfiguration): Promise<Result<string>>
   activatePremiumFeatures(dto: {
     username: string
+    subscriptionId: number
     subscriptionPlanName?: string
     uploadBytesLimit?: number
     endsAt?: Date
+    cancelPreviousSubscription?: boolean
   }): Promise<Result<string>>
   stop(): Promise<Result<string>>
   isRunning(): Promise<boolean>