Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to mock redis cluster for testing in jest or vitest? #1913

Open
SeyyedKhandon opened this issue Sep 6, 2024 · 0 comments
Open

How to mock redis cluster for testing in jest or vitest? #1913

SeyyedKhandon opened this issue Sep 6, 2024 · 0 comments

Comments

@SeyyedKhandon
Copy link

SeyyedKhandon commented Sep 6, 2024

I'm trying to mock ioredis so to be able to test it, but unfortunately, amn not able to do it properly, I used also ioredis-mock but it doesnt trigger any event on ready, so it is not useful for our case. So I wonder how do you mock it in your applications?

This is my connection maker, and I would appreciate any help this:

const createCacheInstance = async () => {
  const address = process.env.REDIS_CLUSTER_ADDRESS
  const password = process.env.REDIS_AUTH_TOKEN

  logger.info(`Redis cluster env address:${address}`)

  try {
    if (!address || !password)
      throw new Error('Environment variables {REDIS_CLUSTER_ADDRESS or REDIS_AUTH_TOKEN} are not available')

    const host = address.split(':')[0]
    const port = Number(address.split(':')[1])
    logger.info(`Redis cluster host:${host} port:${port}`)

    const tlsMode = process.env.NODE_ENV === 'production' ? { tls: {} } : {} // https://github.com/redis/ioredis?tab=readme-ov-file#special-note-aws-elasticache-clusters-with-tls

    const redisCluster = new Redis.Cluster([{ host, port }], {
      slotsRefreshTimeout: 2000, // to prevent [Failed to refresh slots cache error](https://github.com/redis/ioredis/issues/711)
      scaleReads: 'all', // Send write queries to masters and read queries to masters or slaves randomly.
      dnsLookup: (address, callback) => callback(null, address), // https://github.com/redis/ioredis?tab=readme-ov-file#special-note-aws-elasticache-clusters-with-tls
      redisOptions: { password, ...tlsMode },
    })

    return await new Promise<{ redisCluster?: Cluster; error?: Error }>((resolve, reject) => {
      redisCluster
        .on('connecting', () => {
          cacheStatus = redisCluster.status
          logger.info('Connecting to Redis DB')
        })
        .on('connect', () => {
          cacheStatus = redisCluster.status
          logger.info('Connected to Redis DB')
        })
        .on('ready', () => {
          cacheStatus = redisCluster.status
          logger.info('Redis cache instance is ready to use')
          resolve({ redisCluster })
        })
        .on('end', () => {
          cacheStatus = redisCluster.status
          logger.warn('Redis cache connection has been closed')
        })
        .on('error', async (error) => {
          cacheStatus = redisCluster.status
          cacheError = error
          logger.error(error)
          try {
            await redisCluster.quit()
          } catch (e) {
            logger.error({ message: 'Error while closing the Redis cache connection', e })
          }
          reject({ error })
        })
    })
  } catch (e) {
    const error = e as Error
    logger.error(error)
    cacheStatus = 'error'
    cacheError = error
    return { error }
  }
}

What I tried which results in "Connection is closed." "ClusterAllFailedError: Failed to refresh slots cache. which seems I need to mock the connection itself too, but am not sure how?

import { afterAll, describe, expect, it, vi } from 'vitest'
import cacheManager, { useCache } from './index'
import { useHashTagKeyMaker } from './helper'

const mocks = vi.hoisted(() => {
  return {
    redisCluster: vi.fn().mockImplementation(() => ({
      set: vi.fn(),
      get: vi.fn(),
      exists: vi.fn(),
      del: vi.fn(),
      sadd: vi.fn(),
      smembers: vi.fn(),
      srem: vi.fn(),
      scard: vi.fn(),
      smismember: vi.fn(),
      flushdb: vi.fn(),
      nodes: vi.fn().mockReturnValue([{ flushdb: vi.fn() }]),
      on: vi.fn().mockImplementation((event, callback) => {
        if (event === 'ready') {
          callback()
        }
        return this
      }),
      quit: vi.fn(),
      status: 'ready',
    })),
  }
})

vi.mock('ioredis', async () => {
  const actual = await vi.importActual('ioredis')
  return {
    ...actual,
    Cluster: mocks.redisCluster,
  }
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant