import assert from "assert" ; import { Redis } from "ioredis" ; import RedisMock from "ioredis-mock" ; import { IThrottlingMetrics } from "@fluidframework/server-services-core" ; import { TestEngine1 , Lumberjack } from "@fluidframework/server-services-telemetry" ; import { RedisThrottleStorageManager } from "../redisThrottleStorageManager" ; import Sinon from "sinon" ; const lumberjackEngine = new TestEngine1 ( ) ; if ( ! Lumberjack . isSetupCompleted ( ) ) { Lumberjack . setup ( [ lumberjackEngine ] ) ; } describe ( "RedisThrottleStorageManager" , ( ) => { let mockRedisClient ; beforeEach ( ( ) => { Sinon . useFakeTimers ( ) ; mockRedisClient = new RedisMock ( ) as Redis ; } ) ; afterEach ( ( ) => { mockRedisClient . flushall ( ) ; mockRedisClient . quit ( ) ; Sinon . restore ( ) ; } ) ; it ( "Creates and retrieves throttlingMetric" , async ( ) => { const throttleManager = new RedisThrottleStorageManager ( mockRedisClient ) ; const id = "test-id" ; const throttlingMetric = { count : 2 , lastCoolDownAt : Date . now ( ) , throttleStatus : false , throttleReason : "N/A" , retryAfterInMs : 2500 } ; await throttleManager . setThrottlingMetric ( id , throttlingMetric ) ; const retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedThrottlingMetric , throttlingMetric ) ; } ) ; it ( "Creates and overwrites throttlingMetric" , async ( ) => { const throttleManager = new RedisThrottleStorageManager ( mockRedisClient ) ; const id = "test-id" ; const originalThrottlingMetric = { count : 2 , lastCoolDownAt : Date . now ( ) , throttleStatus : false , throttleReason : "N/A" , retryAfterInMs : 0 } ; await throttleManager . setThrottlingMetric ( id , originalThrottlingMetric ) ; const retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedThrottlingMetric , originalThrottlingMetric ) ; const updatedThrottlingMetric = { count : 0 , lastCoolDownAt : Date . now ( ) , throttleStatus : true , throttleReason : "Exceeded token count: Wait 5 seconds" , retryAfterInMs : 5000 } ; await throttleManager . setThrottlingMetric ( id , updatedThrottlingMetric ) ; const retrievedUpdatedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedUpdatedThrottlingMetric , updatedThrottlingMetric ) ; } ) ; it ( "Returns undefined when throttlingMetric does not exist" , async ( ) => { const throttleManager = new RedisThrottleStorageManager ( mockRedisClient ) ; const id = "test-id" ; const retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . strictEqual ( retrievedThrottlingMetric , undefined ) ; } ) ; it ( "Expires outdated values" , async ( ) => { const ttlInSeconds = 10 ; const throttleManager = new RedisThrottleStorageManager ( mockRedisClient , { expireAfterSeconds : ttlInSeconds } ) ; const id = "test-id" ; const originalThrottlingMetric = { count : 2 , lastCoolDownAt : Date . now ( ) , throttleStatus : false , throttleReason : "N/A" , retryAfterInMs : 0 } ; await throttleManager . setThrottlingMetric ( id , originalThrottlingMetric ) ; Sinon . clock . tick ( ttlInSeconds * 1000 - 1 ) ; let retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedThrottlingMetric , originalThrottlingMetric ) ; Sinon . clock . tick ( 1 ) ; retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . strictEqual ( retrievedThrottlingMetric , undefined ) ; } ) ; it ( "Updates expiration on overwrite, then expires outdated values" , async ( ) => { const ttlInSeconds = 10 ; const throttleManager = new RedisThrottleStorageManager ( mockRedisClient , { expireAfterSeconds : ttlInSeconds } ) ; const id = "test-id" ; const originalThrottlingMetric = { count : 2 , lastCoolDownAt : Date . now ( ) , throttleStatus : false , throttleReason : "N/A" , retryAfterInMs : 0 } ; await throttleManager . setThrottlingMetric ( id , originalThrottlingMetric ) ; Sinon . clock . tick ( ttlInSeconds * 1000 - 1 ) ; let retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedThrottlingMetric , originalThrottlingMetric ) ; const updatedThrottlingMetric = { ... originalThrottlingMetric , count : 3 } ; await throttleManager . setThrottlingMetric ( id , updatedThrottlingMetric ) ; Sinon . clock . tick ( ttlInSeconds * 1000 - 1 ) ; retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . deepStrictEqual ( retrievedThrottlingMetric , updatedThrottlingMetric ) ; Sinon . clock . tick ( 1 ) ; retrievedThrottlingMetric = await throttleManager . getThrottlingMetric ( id ) ; assert . strictEqual ( retrievedThrottlingMetric , undefined ) ; } ) ; } ) ;