yeahoffline / redis-mock

Node.js redis client mock
211 stars 110 forks source link

Set then Get fails #197

Open fishcharlie opened 2 years ago

fishcharlie commented 2 years ago

The following code should print bar to the console. However, instead it prints undefined.

Running redis-mock version 0.56.3.

var redis = require("redis-mock"),
    client = redis.createClient();

(async () => {
    try {
        await client.set("foo", "bar");
        console.log(await client.get("foo"));
    } catch (e) {
        console.log(e);
    }
})();
jason-ransom-slalom commented 2 years ago

Curious about this as well. Experiencing the same behavior.

cohendvir commented 2 years ago

any updates on this one? this actually prevents my from using this package

rbrcurtis commented 1 year ago

It's because redis-mock only supports the callback syntax for redis not async/await, which was removed in the latest major version of the redis package. This works:

  let redis = require('redis-mock').createClient()
  await new Promise<void>((resolve) => {
    redis.set('foo', 'bar', () => {
      log('set complete')
      redis.get('foo', (err, val) => {
        log('get complete', val)
        resolve()
      })
    })
  })

and as an aside, the latest version of @types/redis-mock is incorrect.

rbrcurtis commented 1 year ago

Here's a kind of smelly workaround:

  let redis = require('redis-mock').createClient()
  redis.set = promisify(redis.set.bind(redis))
  redis.get = promisify(redis.get.bind(redis))
  await redis.set('foo', 'bar')
  log('set complete')
  let val = await redis.get('foo')
  log('get complete', val)
eatoncw commented 8 months ago

Building from @rbrcurtis 's workaround:

I had to create a custom promisify function for the set method to be able to pass additional args, for example client.set("key", "value", {EX: ttl}).

Because this library has a set method where the last argument is not actually the callback, but args = {}, this was required to be able to pass additional args and it not throw.

I'm sure there are better ways to do this (l couldn't get util/promisify custom to work), and this now means I can't mock ttl, but this at least got me started.

I put this all in jest setup file:

import util from 'node:util';
jest.mock('redis', () => jest.requireActual('redis-mock'));

beforeAll(() => {
  let redis = require('./server/lib/redis/client').redisClient;
  redis.set = customPromisify(redis.set.bind(redis));
  redis.get = util.promisify(redis.get.bind(redis));
});

function customPromisify(f) {
  return function (...args) {
    args.pop(); // just removing the last argument basically
    return new Promise((resolve, reject) => {
      function cb(err, result) {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      }

      args.push(cb);

      f.call(this, ...args);
    });
  };
}
dvqc commented 3 months ago

Building from @rbrcurtis 's workaround:

I had to create a custom promisify function for the set method to be able to pass additional args, for example client.set("key", "value", {EX: ttl}).

Because this library has a set method where the last argument is not actually the callback, but args = {}, this was required to be able to pass additional args and it not throw.

I'm sure there are better ways to do this (l couldn't get util/promisify custom to work), and this now means I can't mock ttl, but this at least got me started.

I put this all in jest setup file:

import util from 'node:util';
jest.mock('redis', () => jest.requireActual('redis-mock'));

beforeAll(() => {
  let redis = require('./server/lib/redis/client').redisClient;
  redis.set = customPromisify(redis.set.bind(redis));
  redis.get = util.promisify(redis.get.bind(redis));
});

function customPromisify(f) {
  return function (...args) {
    args.pop(); // just removing the last argument basically
    return new Promise((resolve, reject) => {
      function cb(err, result) {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      }

      args.push(cb);

      f.call(this, ...args);
    });
  };
}

@eatoncw The customPromisify function should have a check for args length in case redis.set is called without the additional args. Something like this:

if (args.length > 2)
    args.pop();