@@ -14,12 +14,15 @@ import {
1414} from '../../../test/fixtures' ;
1515import { SequelizeWorkspaceRepository } from '../workspaces/repositories/workspaces.repository' ;
1616import { SequelizeUserRepository } from '../user/user.repository' ;
17+ import { CacheManagerService } from '../cache-manager/cache-manager.service' ;
18+ import { PaymentRequiredException } from './exceptions/payment-required.exception' ;
1719
1820describe ( 'FeatureLimitService' , ( ) => {
1921 let service : FeatureLimitService ;
2022 let limitsRepository : DeepMocked < SequelizeFeatureLimitsRepository > ;
2123 let workspaceRepository : DeepMocked < SequelizeWorkspaceRepository > ;
2224 let userRepository : DeepMocked < SequelizeUserRepository > ;
25+ let cacheManagerService : DeepMocked < CacheManagerService > ;
2326
2427 beforeEach ( async ( ) => {
2528 const moduleRef = await Test . createTestingModule ( {
@@ -33,6 +36,7 @@ describe('FeatureLimitService', () => {
3336 limitsRepository = moduleRef . get ( SequelizeFeatureLimitsRepository ) ;
3437 workspaceRepository = moduleRef . get ( SequelizeWorkspaceRepository ) ;
3538 userRepository = moduleRef . get ( SequelizeUserRepository ) ;
39+ cacheManagerService = moduleRef . get ( CacheManagerService ) ;
3640 } ) ;
3741
3842 describe ( 'canUserAccessPlatform' , ( ) => {
@@ -370,6 +374,171 @@ describe('FeatureLimitService', () => {
370374 } ) ;
371375 } ) ;
372376
377+ describe ( 'enforceMaxUploadFileSize' , ( ) => {
378+ const MB = 1024 * 1024 ;
379+ const GB = 1024 * MB ;
380+
381+ it ( 'When user has no limit configured, then it should allow the upload' , async ( ) => {
382+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
383+
384+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
385+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
386+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( null ) ;
387+
388+ await expect (
389+ service . enforceMaxUploadFileSize ( user , BigInt ( 500 * MB ) ) ,
390+ ) . resolves . not . toThrow ( ) ;
391+ } ) ;
392+
393+ it ( 'When file size is under tier limit, then it should allow the upload' , async ( ) => {
394+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
395+ const limit = newFeatureLimit ( {
396+ type : LimitTypes . Counter ,
397+ label : LimitLabels . MaxUploadFileSize ,
398+ value : String ( 100 * MB ) ,
399+ } ) ;
400+
401+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
402+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
403+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( limit ) ;
404+
405+ await expect (
406+ service . enforceMaxUploadFileSize ( user , BigInt ( 50 * MB ) ) ,
407+ ) . resolves . not . toThrow ( ) ;
408+ } ) ;
409+
410+ it ( 'When file size equals tier limit, then it should throw PaymentRequiredException' , async ( ) => {
411+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
412+ const limit = newFeatureLimit ( {
413+ type : LimitTypes . Counter ,
414+ label : LimitLabels . MaxUploadFileSize ,
415+ value : String ( 100 * MB ) ,
416+ } ) ;
417+
418+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
419+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
420+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( limit ) ;
421+
422+ await expect (
423+ service . enforceMaxUploadFileSize ( user , BigInt ( 100 * MB ) ) ,
424+ ) . rejects . toThrow ( PaymentRequiredException ) ;
425+ } ) ;
426+
427+ it ( 'When file size exceeds tier limit, then it should throw PaymentRequiredException' , async ( ) => {
428+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
429+ const limit = newFeatureLimit ( {
430+ type : LimitTypes . Counter ,
431+ label : LimitLabels . MaxUploadFileSize ,
432+ value : String ( 100 * MB ) ,
433+ } ) ;
434+
435+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
436+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
437+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( limit ) ;
438+
439+ await expect (
440+ service . enforceMaxUploadFileSize ( user , BigInt ( 200 * MB ) ) ,
441+ ) . rejects . toThrow ( PaymentRequiredException ) ;
442+ } ) ;
443+
444+ it ( 'When user has an overridden limit, then it should use it instead of tier limit' , async ( ) => {
445+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
446+ const override = newFeatureLimit ( {
447+ type : LimitTypes . Counter ,
448+ label : LimitLabels . MaxUploadFileSize ,
449+ value : String ( 10 * GB ) ,
450+ } ) ;
451+
452+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( override ) ;
453+
454+ await expect (
455+ service . enforceMaxUploadFileSize ( user , BigInt ( 5 * GB ) ) ,
456+ ) . resolves . not . toThrow ( ) ;
457+ expect ( cacheManagerService . getTierLimit ) . not . toHaveBeenCalled ( ) ;
458+ expect ( limitsRepository . findLimitByLabelAndTier ) . not . toHaveBeenCalled ( ) ;
459+ } ) ;
460+
461+ it ( 'When user overridden limit is exceeded, then it should throw' , async ( ) => {
462+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
463+ const override = newFeatureLimit ( {
464+ type : LimitTypes . Counter ,
465+ label : LimitLabels . MaxUploadFileSize ,
466+ value : String ( 100 * MB ) ,
467+ } ) ;
468+
469+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( override ) ;
470+
471+ await expect (
472+ service . enforceMaxUploadFileSize ( user , BigInt ( 200 * MB ) ) ,
473+ ) . rejects . toThrow ( PaymentRequiredException ) ;
474+ } ) ;
475+
476+ it ( 'When tier limit is cached, then it should not hit the DB' , async ( ) => {
477+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
478+
479+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
480+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( String ( 1 * GB ) ) ;
481+
482+ await expect (
483+ service . enforceMaxUploadFileSize ( user , BigInt ( 500 * MB ) ) ,
484+ ) . resolves . not . toThrow ( ) ;
485+ expect ( limitsRepository . findLimitByLabelAndTier ) . not . toHaveBeenCalled ( ) ;
486+ } ) ;
487+
488+ it ( 'When tier limit is cached and exceeded, then it should throw without hitting DB' , async ( ) => {
489+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
490+
491+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
492+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( String ( 100 * MB ) ) ;
493+
494+ await expect (
495+ service . enforceMaxUploadFileSize ( user , BigInt ( 200 * MB ) ) ,
496+ ) . rejects . toThrow ( PaymentRequiredException ) ;
497+ expect ( limitsRepository . findLimitByLabelAndTier ) . not . toHaveBeenCalled ( ) ;
498+ } ) ;
499+
500+ it ( 'When cache miss occurs, then it should populate the cache from DB' , async ( ) => {
501+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
502+ const limit = newFeatureLimit ( {
503+ type : LimitTypes . Counter ,
504+ label : LimitLabels . MaxUploadFileSize ,
505+ value : String ( 1 * GB ) ,
506+ } ) ;
507+
508+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
509+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
510+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( limit ) ;
511+
512+ await service . enforceMaxUploadFileSize ( user , BigInt ( 500 * MB ) ) ;
513+
514+ expect ( cacheManagerService . setTierLimit ) . toHaveBeenCalledWith (
515+ user . tierId ,
516+ LimitLabels . MaxUploadFileSize ,
517+ limit . value ,
518+ ) ;
519+ } ) ;
520+
521+ it ( 'When cache write fails, then it should still allow the upload' , async ( ) => {
522+ const user = newUser ( { attributes : { tierId : v4 ( ) } } ) ;
523+ const limit = newFeatureLimit ( {
524+ type : LimitTypes . Counter ,
525+ label : LimitLabels . MaxUploadFileSize ,
526+ value : String ( 1 * GB ) ,
527+ } ) ;
528+
529+ limitsRepository . findUserOverriddenLimit . mockResolvedValueOnce ( null ) ;
530+ cacheManagerService . getTierLimit . mockResolvedValueOnce ( null ) ;
531+ limitsRepository . findLimitByLabelAndTier . mockResolvedValueOnce ( limit ) ;
532+ cacheManagerService . setTierLimit . mockRejectedValueOnce (
533+ new Error ( 'Redis unavailable' ) ,
534+ ) ;
535+
536+ await expect (
537+ service . enforceMaxUploadFileSize ( user , BigInt ( 500 * MB ) ) ,
538+ ) . resolves . not . toThrow ( ) ;
539+ } ) ;
540+ } ) ;
541+
373542 describe ( 'getTier' , ( ) => {
374543 it ( 'When tier exists, then it should return the tier' , async ( ) => {
375544 const tierId = v4 ( ) ;
0 commit comments