@@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
22import { when } from 'jest-when' ;
33import { IRedisClusterNodeAddress , ReplyError } from 'src/models' ;
44import {
5+ mockFeatureService ,
56 mockIOClusterNode1 ,
67 mockIOClusterNode2 ,
78 mockIORedisClient ,
@@ -17,14 +18,15 @@ import {
1718 mockSentinelMasterEndpoint ,
1819 mockSentinelMasterInDownState ,
1920 mockSentinelMasterInOkState ,
20- mockStandaloneRedisInfoReply ,
21+ mockStandaloneRedisInfoReply , MockType ,
2122} from 'src/__mocks__' ;
2223import { REDIS_MODULES_COMMANDS , AdditionalRedisModuleName } from 'src/constants' ;
2324import { DatabaseInfoProvider } from 'src/modules/database/providers/database-info.provider' ;
2425import { RedisDatabaseInfoResponse } from 'src/modules/database/dto/redis-info.dto' ;
2526import { BadRequestException , ForbiddenException } from '@nestjs/common' ;
2627import { SentinelMasterStatus } from 'src/modules/redis-sentinel/models/sentinel-master' ;
2728import ERROR_MESSAGES from 'src/constants/error-messages' ;
29+ import { FeatureService } from 'src/modules/feature/feature.service' ;
2830
2931const mockClusterNodeAddresses : IRedisClusterNodeAddress [ ] = [
3032 {
@@ -78,14 +80,22 @@ const mockSentinelConnectionOptions = {
7880
7981describe ( 'DatabaseInfoProvider' , ( ) => {
8082 let service : DatabaseInfoProvider ;
83+ let featureService : MockType < FeatureService > ;
8184
8285 beforeEach ( async ( ) => {
8386 jest . clearAllMocks ( ) ;
8487 const module : TestingModule = await Test . createTestingModule ( {
85- providers : [ DatabaseInfoProvider ] ,
88+ providers : [
89+ DatabaseInfoProvider ,
90+ {
91+ provide : FeatureService ,
92+ useFactory : mockFeatureService ,
93+ } ,
94+ ] ,
8695 } ) . compile ( ) ;
8796
8897 service = await module . get ( DatabaseInfoProvider ) ;
98+ featureService = await module . get ( FeatureService ) ;
8999 } ) ;
90100
91101 describe ( 'isCluster' , ( ) => {
@@ -239,7 +249,7 @@ describe('DatabaseInfoProvider', () => {
239249 } ) ;
240250
241251 describe ( 'determineDatabaseModules' , ( ) => {
242- it ( 'get modules by using MODULE LIST command' , async ( ) => {
252+ it ( 'get modules by using MODULE LIST command (without filters) ' , async ( ) => {
243253 when ( mockIORedisClient . call )
244254 . calledWith ( 'module' , [ 'list' ] )
245255 . mockResolvedValue ( mockRedisModuleList ) ;
@@ -258,7 +268,37 @@ describe('DatabaseInfoProvider', () => {
258268 { name : 'customModule' , version : 10000 , semanticVersion : undefined } ,
259269 ] ) ;
260270 } ) ;
261- it ( 'detect all modules by using COMMAND INFO command' , async ( ) => {
271+ it ( 'get modules by using MODULE LIST command (with filters applied)' , async ( ) => {
272+ when ( mockIORedisClient . call )
273+ . calledWith ( 'module' , [ 'list' ] )
274+ . mockResolvedValue ( mockRedisModuleList ) ;
275+ featureService . getByName . mockResolvedValue ( {
276+ flag : true ,
277+ data : {
278+ hideByName : [
279+ {
280+ expression : 'rejSoN' ,
281+ options : 'i' ,
282+ } ,
283+ ] ,
284+ } ,
285+ } ) ;
286+
287+ const result = await service . determineDatabaseModules ( mockIORedisClient ) ;
288+
289+ expect ( mockIORedisClient . call ) . not . toHaveBeenCalledWith ( 'command' , expect . anything ( ) ) ;
290+ expect ( result ) . toEqual ( [
291+ { name : AdditionalRedisModuleName . RedisAI , version : 10000 , semanticVersion : '1.0.0' } ,
292+ { name : AdditionalRedisModuleName . RedisGraph , version : 10000 , semanticVersion : '1.0.0' } ,
293+ { name : AdditionalRedisModuleName . RedisGears , version : 10000 , semanticVersion : '1.0.0' } ,
294+ { name : AdditionalRedisModuleName . RedisBloom , version : 10000 , semanticVersion : '1.0.0' } ,
295+ // { name: AdditionalRedisModuleName.RedisJSON, version: 10000, semanticVersion: '1.0.0' }, should be ignored
296+ { name : AdditionalRedisModuleName . RediSearch , version : 10000 , semanticVersion : '1.0.0' } ,
297+ { name : AdditionalRedisModuleName . RedisTimeSeries , version : 10000 , semanticVersion : '1.0.0' } ,
298+ { name : 'customModule' , version : 10000 , semanticVersion : undefined } ,
299+ ] ) ;
300+ } ) ;
301+ it ( 'detect all modules by using COMMAND INFO command (without filter)' , async ( ) => {
262302 when ( mockIORedisClient . call )
263303 . calledWith ( 'module' , [ 'list' ] )
264304 . mockRejectedValue ( mockUnknownCommandModule ) ;
@@ -282,6 +322,41 @@ describe('DatabaseInfoProvider', () => {
282322 { name : AdditionalRedisModuleName . RedisTimeSeries } ,
283323 ] ) ;
284324 } ) ;
325+ it ( 'detect all modules by using COMMAND INFO command (with filter)' , async ( ) => {
326+ when ( mockIORedisClient . call )
327+ . calledWith ( 'module' , [ 'list' ] )
328+ . mockRejectedValue ( mockUnknownCommandModule ) ;
329+ when ( mockIORedisClient . call )
330+ . calledWith ( 'command' , expect . anything ( ) )
331+ . mockResolvedValue ( [
332+ null ,
333+ [ 'somecommand' , - 1 , [ 'readonly' ] , 0 , 0 , - 1 , [ ] ] ,
334+ ] ) ;
335+ featureService . getByName . mockResolvedValue ( {
336+ flag : true ,
337+ data : {
338+ hideByName : [
339+ {
340+ expression : 'rejSoN' ,
341+ options : 'i' ,
342+ } ,
343+ ] ,
344+ } ,
345+ } ) ;
346+
347+ const result = await service . determineDatabaseModules ( mockIORedisClient ) ;
348+
349+ expect ( mockIORedisClient . call ) . toHaveBeenCalledTimes ( REDIS_MODULES_COMMANDS . size + 1 ) ;
350+ expect ( result ) . toEqual ( [
351+ { name : AdditionalRedisModuleName . RedisAI } ,
352+ { name : AdditionalRedisModuleName . RedisGraph } ,
353+ { name : AdditionalRedisModuleName . RedisGears } ,
354+ { name : AdditionalRedisModuleName . RedisBloom } ,
355+ // { name: AdditionalRedisModuleName.RedisJSON }, should be ignored
356+ { name : AdditionalRedisModuleName . RediSearch } ,
357+ { name : AdditionalRedisModuleName . RedisTimeSeries } ,
358+ ] ) ;
359+ } ) ;
285360 it ( 'detect only RediSearch module by using COMMAND INFO command' , async ( ) => {
286361 when ( mockIORedisClient . call )
287362 . calledWith ( 'module' , [ 'list' ] )
@@ -372,7 +447,7 @@ describe('DatabaseInfoProvider', () => {
372447 } ) ;
373448 it ( 'should throw an error if no permission to run \'info\' command' , async ( ) => {
374449 mockIORedisClient . info . mockRejectedValue ( {
375- message : 'NOPERM this user has no permissions to run the \'info\' command'
450+ message : 'NOPERM this user has no permissions to run the \'info\' command' ,
376451 } ) ;
377452
378453 try {
0 commit comments