From 72edd174d0f89f97a47cc296342431595d77255b Mon Sep 17 00:00:00 2001 From: rishtigupta Date: Tue, 16 Apr 2024 12:17:10 -0700 Subject: [PATCH 1/5] feat: add incr api --- src/momento-redis-adapter.ts | 16 +++++++++ test/increment.test.ts | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/increment.test.ts diff --git a/src/momento-redis-adapter.ts b/src/momento-redis-adapter.ts index 94831c6..671ebdd 100644 --- a/src/momento-redis-adapter.ts +++ b/src/momento-redis-adapter.ts @@ -9,6 +9,7 @@ import { CacheDictionarySetFields, CacheFlush, CacheGet, + CacheIncrement, CacheItemGetTtl, CacheSet, CacheSetIfAbsent, @@ -91,6 +92,8 @@ export interface MomentoIORedis { nx: 'NX' ): Promise<'OK' | null>; + incr(key: RedisKey): Promise; + ttl(key: RedisKey): Promise; pttl(key: RedisKey): Promise; @@ -395,6 +398,19 @@ export class MomentoRedisAdapter return null; } + async incr(key: RedisKey): Promise { + const rsp = await this.momentoClient.increment(this.cacheName, key); + if (rsp instanceof CacheIncrement.Success) { + return rsp.value(); + } else if (rsp instanceof CacheIncrement.Error) { + this.emitError('incr', rsp.message(), rsp.errorCode()); + return rsp.message(); + } else { + this.emitError('incr', `unexpected-response ${rsp.toString()}`); + return rsp.toString(); + } + } + async hset( ...args: [ RedisKey, diff --git a/test/increment.test.ts b/test/increment.test.ts new file mode 100644 index 0000000..60f5c22 --- /dev/null +++ b/test/increment.test.ts @@ -0,0 +1,70 @@ +import {SetupIntegrationTest} from './integration-setup'; +import {v4} from 'uuid'; + +const {client} = SetupIntegrationTest(); + +describe('increment', () => { + it('should increment the value of the key by 1 if the key exists', async () => { + const key = v4(); + const value = 5; + + // Set initial key value + await client.set(key, value); + + // Increment the value of the key + const incrResp = await client.incr(key); + expect(incrResp).toBe(value + 1); + }); + + it('should increment the value of the key to 1 if the key does not exists', async () => { + const key = v4(); + + // Increment the value of the key that is not set + const incrResp = await client.incr(key); + expect(incrResp).toBe(1); + }); + + it('should error out if the key contains a value of wrong type or contains a string that can be represented as integer', async () => { + const key = v4(); + const value = 'monkey'; + + // Set initial key value + await client.set(key, value); + + // Increment the value of the key that is not set + try { + await client.incr(key); + } catch (error) { + if (process.env.MOMENTO_ENABLED === 'true') { + const momentoError = error as { + code: string; + context: {code: string; msg: string; op: string; platform: string}; + }; + expect(momentoError.code).toBe('ERR_UNHANDLED_ERROR'); + expect(momentoError.context.code).toBe('FAILED_PRECONDITION_ERROR'); + expect(momentoError.context.msg).toBe( + "System is not in a state required for the operation's execution: 9 FAILED_PRECONDITION: failed to parse value into long" + ); + expect(momentoError.context.op).toBe('incr'); + expect(momentoError.context.platform).toBe('momento'); + } else { + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe( + 'ERR value is not an integer or out of range' + ); + } + } + }); + + it('should increment the value of key that contains a string that can be represented as integer', async () => { + const key = v4(); + const value = '10'; + + // Set initial key value + await client.set(key, value); + + // Increment the value of the key that is not set + const incrResp = await client.incr(key); + expect(incrResp).toBe(11); + }); +}); From b05a0bba028a6d583fa0d018d169a2b274c618bc Mon Sep 17 00:00:00 2001 From: rishtigupta Date: Tue, 16 Apr 2024 16:23:10 -0700 Subject: [PATCH 2/5] fix: return error if compression client is used for incr operation --- src/momento-redis-adapter.ts | 12 ++++++++---- test/increment-compression.test.ts | 28 ++++++++++++++++++++++++++++ test/increment.test.ts | 2 +- test/integration-setup.ts | 12 ++++-------- 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 test/increment-compression.test.ts diff --git a/src/momento-redis-adapter.ts b/src/momento-redis-adapter.ts index 671ebdd..6b7064c 100644 --- a/src/momento-redis-adapter.ts +++ b/src/momento-redis-adapter.ts @@ -92,7 +92,7 @@ export interface MomentoIORedis { nx: 'NX' ): Promise<'OK' | null>; - incr(key: RedisKey): Promise; + incr(key: RedisKey): Promise; ttl(key: RedisKey): Promise; @@ -398,17 +398,21 @@ export class MomentoRedisAdapter return null; } - async incr(key: RedisKey): Promise { + async incr(key: RedisKey): Promise { + if (this.useCompression) { + this.emitError('incr', 'compression-not-supported'); + return null; + } + const rsp = await this.momentoClient.increment(this.cacheName, key); if (rsp instanceof CacheIncrement.Success) { return rsp.value(); } else if (rsp instanceof CacheIncrement.Error) { this.emitError('incr', rsp.message(), rsp.errorCode()); - return rsp.message(); } else { this.emitError('incr', `unexpected-response ${rsp.toString()}`); - return rsp.toString(); } + return null; } async hset( diff --git a/test/increment-compression.test.ts b/test/increment-compression.test.ts new file mode 100644 index 0000000..c3b9adc --- /dev/null +++ b/test/increment-compression.test.ts @@ -0,0 +1,28 @@ +import {v4} from 'uuid'; + +import {SetupIntegrationTest} from './integration-setup'; + +const {client} = SetupIntegrationTest(); + +describe('increment, with compression client', () => { + it('should return error saying compression not supported', async () => { + const key = v4(); + const value = 5; + + // Set initial key value + await client.set(key, value); + + // Increment the value of the key + try { + await client.incr(key); + } catch (error) { + const momentoError = error as { + code: string; + context: {code: string; msg: string; op: string; platform: string}; + }; + expect(momentoError.context.op).toBe('incr'); + expect(momentoError.context.platform).toBe('momento'); + expect(momentoError.context.msg).toBe('compression-not-supported'); + } + }); +}); diff --git a/test/increment.test.ts b/test/increment.test.ts index 60f5c22..e5ea4c8 100644 --- a/test/increment.test.ts +++ b/test/increment.test.ts @@ -1,7 +1,7 @@ import {SetupIntegrationTest} from './integration-setup'; import {v4} from 'uuid'; -const {client} = SetupIntegrationTest(); +const {client} = SetupIntegrationTest(false); describe('increment', () => { it('should increment the value of the key by 1 if the key exists', async () => { diff --git a/test/integration-setup.ts b/test/integration-setup.ts index fd941c3..fe9f103 100644 --- a/test/integration-setup.ts +++ b/test/integration-setup.ts @@ -16,10 +16,6 @@ export function testCacheName(): string { return name + v4(); } -function useCompression(): boolean { - return process.env.COMPRESSION === 'true'; -} - const deleteCacheIfExists = async (momento: CacheClient, cacheName: string) => { const deleteResponse = await momento.deleteCache(cacheName); if (deleteResponse instanceof DeleteCache.Error) { @@ -45,17 +41,17 @@ export function isRedisBackedTest() { return process.env.MOMENTO_ENABLED !== 'true'; } -export function SetupIntegrationTest(): { +export function SetupIntegrationTest(useCompression = true): { client: MomentoIORedis; } { if (isRedisBackedTest()) { return setupIntegrationTestWithRedis(); } else { - return setupIntegrationTestWithMomento(); + return setupIntegrationTestWithMomento(useCompression); } } -function setupIntegrationTestWithMomento() { +function setupIntegrationTestWithMomento(useCompression = true) { const cacheName = testCacheName(); beforeAll(async () => { @@ -82,7 +78,7 @@ function setupIntegrationTestWithMomento() { momentoClient, cacheName, { - useCompression: useCompression(), + useCompression: useCompression, } ); From 5fcf4f93b01b7b9a740a81f077e25c605f5716d5 Mon Sep 17 00:00:00 2001 From: rishtigupta Date: Wed, 17 Apr 2024 10:25:21 -0700 Subject: [PATCH 3/5] fix; remove useCompression arg from integ setup function --- test/increment-compression.test.ts | 32 ++++++++++++++++-------------- test/increment.test.ts | 2 +- test/integration-setup.ts | 12 +++++++---- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/test/increment-compression.test.ts b/test/increment-compression.test.ts index c3b9adc..2f18dbc 100644 --- a/test/increment-compression.test.ts +++ b/test/increment-compression.test.ts @@ -6,23 +6,25 @@ const {client} = SetupIntegrationTest(); describe('increment, with compression client', () => { it('should return error saying compression not supported', async () => { - const key = v4(); - const value = 5; + if (process.env.COMPRESSION === 'true') { + const key = v4(); + const value = 5; - // Set initial key value - await client.set(key, value); + // Set initial key value + await client.set(key, value); - // Increment the value of the key - try { - await client.incr(key); - } catch (error) { - const momentoError = error as { - code: string; - context: {code: string; msg: string; op: string; platform: string}; - }; - expect(momentoError.context.op).toBe('incr'); - expect(momentoError.context.platform).toBe('momento'); - expect(momentoError.context.msg).toBe('compression-not-supported'); + // Increment the value of the key + try { + await client.incr(key); + } catch (error) { + const momentoError = error as { + code: string; + context: {code: string; msg: string; op: string; platform: string}; + }; + expect(momentoError.context.op).toBe('incr'); + expect(momentoError.context.platform).toBe('momento'); + expect(momentoError.context.msg).toBe('compression-not-supported'); + } } }); }); diff --git a/test/increment.test.ts b/test/increment.test.ts index e5ea4c8..60f5c22 100644 --- a/test/increment.test.ts +++ b/test/increment.test.ts @@ -1,7 +1,7 @@ import {SetupIntegrationTest} from './integration-setup'; import {v4} from 'uuid'; -const {client} = SetupIntegrationTest(false); +const {client} = SetupIntegrationTest(); describe('increment', () => { it('should increment the value of the key by 1 if the key exists', async () => { diff --git a/test/integration-setup.ts b/test/integration-setup.ts index fe9f103..fd941c3 100644 --- a/test/integration-setup.ts +++ b/test/integration-setup.ts @@ -16,6 +16,10 @@ export function testCacheName(): string { return name + v4(); } +function useCompression(): boolean { + return process.env.COMPRESSION === 'true'; +} + const deleteCacheIfExists = async (momento: CacheClient, cacheName: string) => { const deleteResponse = await momento.deleteCache(cacheName); if (deleteResponse instanceof DeleteCache.Error) { @@ -41,17 +45,17 @@ export function isRedisBackedTest() { return process.env.MOMENTO_ENABLED !== 'true'; } -export function SetupIntegrationTest(useCompression = true): { +export function SetupIntegrationTest(): { client: MomentoIORedis; } { if (isRedisBackedTest()) { return setupIntegrationTestWithRedis(); } else { - return setupIntegrationTestWithMomento(useCompression); + return setupIntegrationTestWithMomento(); } } -function setupIntegrationTestWithMomento(useCompression = true) { +function setupIntegrationTestWithMomento() { const cacheName = testCacheName(); beforeAll(async () => { @@ -78,7 +82,7 @@ function setupIntegrationTestWithMomento(useCompression = true) { momentoClient, cacheName, { - useCompression: useCompression, + useCompression: useCompression(), } ); From c47f6dd8367ed4b36b8453605a55fa36a7f77bf2 Mon Sep 17 00:00:00 2001 From: rishtigupta Date: Wed, 17 Apr 2024 10:33:17 -0700 Subject: [PATCH 4/5] fix: skip increment tests if compression is enabled --- test/increment.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/increment.test.ts b/test/increment.test.ts index 60f5c22..e698b31 100644 --- a/test/increment.test.ts +++ b/test/increment.test.ts @@ -4,6 +4,12 @@ import {v4} from 'uuid'; const {client} = SetupIntegrationTest(); describe('increment', () => { + if (process.env.COMPRESSION === 'true') { + test.skip('all tests skipped because COMPRESSION is enabled', () => { + // Skip all tests, no assertions will be called + }); + return; + } it('should increment the value of the key by 1 if the key exists', async () => { const key = v4(); const value = 5; From daea017e2f49c184e0a63965a6b0bc06b8eb515a Mon Sep 17 00:00:00 2001 From: rishtigupta Date: Thu, 18 Apr 2024 09:42:02 -0700 Subject: [PATCH 5/5] fix: refactor tests --- src/momento-redis-adapter.ts | 5 ++++- test/increment-compression.test.ts | 8 +++++--- test/increment.test.ts | 4 ++-- test/integration-setup.ts | 6 +++++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/momento-redis-adapter.ts b/src/momento-redis-adapter.ts index 6b7064c..bc3ddd3 100644 --- a/src/momento-redis-adapter.ts +++ b/src/momento-redis-adapter.ts @@ -400,7 +400,10 @@ export class MomentoRedisAdapter async incr(key: RedisKey): Promise { if (this.useCompression) { - this.emitError('incr', 'compression-not-supported'); + this.emitError( + 'incr', + 'Increment is not supported when compression is enabled.' + ); return null; } diff --git a/test/increment-compression.test.ts b/test/increment-compression.test.ts index 2f18dbc..e797125 100644 --- a/test/increment-compression.test.ts +++ b/test/increment-compression.test.ts @@ -2,11 +2,11 @@ import {v4} from 'uuid'; import {SetupIntegrationTest} from './integration-setup'; -const {client} = SetupIntegrationTest(); +const {client, compression} = SetupIntegrationTest(); describe('increment, with compression client', () => { it('should return error saying compression not supported', async () => { - if (process.env.COMPRESSION === 'true') { + if (compression) { const key = v4(); const value = 5; @@ -23,7 +23,9 @@ describe('increment, with compression client', () => { }; expect(momentoError.context.op).toBe('incr'); expect(momentoError.context.platform).toBe('momento'); - expect(momentoError.context.msg).toBe('compression-not-supported'); + expect(momentoError.context.msg).toBe( + 'Increment is not supported when compression is enabled.' + ); } } }); diff --git a/test/increment.test.ts b/test/increment.test.ts index e698b31..7bd3d49 100644 --- a/test/increment.test.ts +++ b/test/increment.test.ts @@ -1,10 +1,10 @@ import {SetupIntegrationTest} from './integration-setup'; import {v4} from 'uuid'; -const {client} = SetupIntegrationTest(); +const {client, compression} = SetupIntegrationTest(); describe('increment', () => { - if (process.env.COMPRESSION === 'true') { + if (compression) { test.skip('all tests skipped because COMPRESSION is enabled', () => { // Skip all tests, no assertions will be called }); diff --git a/test/integration-setup.ts b/test/integration-setup.ts index fd941c3..d8e5b84 100644 --- a/test/integration-setup.ts +++ b/test/integration-setup.ts @@ -47,6 +47,7 @@ export function isRedisBackedTest() { export function SetupIntegrationTest(): { client: MomentoIORedis; + compression?: boolean; } { if (isRedisBackedTest()) { return setupIntegrationTestWithRedis(); @@ -86,7 +87,10 @@ function setupIntegrationTestWithMomento() { } ); - return {client: momentoNodeRedisClient}; + return { + client: momentoNodeRedisClient, + compression: momentoNodeRedisClient.useCompression, + }; } function setupIntegrationTestWithRedis() {