Skip to content

Bug: SCAN commands have results in lua script indexed at 0, missing first element when iterating #1345

Description

@moritzraho

Reproduce

From the REPL (> node)

Redis = require('ioredis-mock'); redis = new Redis()
redis.defineCommand('scanWrapper', { lua: `local res = redis.call('SCAN', '0') for idx, member in ipairs(res[2]) do print(idx, member) end return res[2]` })
await redis.mset('a', '1', 'b', '2'); await redis.scanWrapper()

prints

1       b
at index 0 (wrong)      a

instead of expected

1        a
2       b
at index 0 (wrong)      nil

and returns

['b']

instead of

['a', 'b']

Patch (dirty)

for bundled v8.9.0 (with npx patch-package ioredis-mock)

diff --git a/node_modules/ioredis-mock/lib/index.js b/node_modules/ioredis-mock/lib/index.js
index a82c297..ac6fd63 100644
--- a/node_modules/ioredis-mock/lib/index.js
+++ b/node_modules/ioredis-mock/lib/index.js
@@ -3748,6 +3748,16 @@ var import_fengari = __toESM(require("fengari")), import_fengari_interop = __toE
 }, makeReturnValue = (L) => {
   if (!isTopArray(L)()) {
     let retVal2 = import_fengari_interop.default.tojs(L, -1);
+    // hack to support *scan commands
+    if (lua.lua_istable(L, -1)) {
+      lua.lua_len(L, -1);
+      let length = lua.lua_tointeger(L, -1);
+      retValArray = []
+      for (let i = 1; i <= length; i++) {
+        retValArray.push(retVal2.get(i))
+      }
+      return retValArray
+    }
     return Array.isArray(retVal2) ? retVal2.slice(1) : retVal2;
   }
   let arrayLength = getTopLength(L), table = import_fengari_interop.default.tojs(L, -1), retVal = [];
@@ -3830,9 +3840,15 @@ var { lua: lua2, to_luastring: toLuaString2 } = import_fengari2.default, defineR
       throw err;
     return import_fengari_interop2.default.push(vm.L, ["error", err.toString()]), 1;
   }
-  return result || result === 0 ? (Array.isArray(result) && (result.unshift(null), result[Symbol.for("__len")] = function() {
+  return result || result === 0 ? (
+    Array.isArray(result) && (result.unshift(null), result[Symbol.for("__len")] = function() {
+    return this.length - 1;
+  }),
+  Array.isArray(result[2]) && (result[2].unshift(null), result[2][Symbol.for("__len")] = function() {
     return this.length - 1;
-  }), import_fengari_interop2.default.push(vm.L, result), 1) : 0;
+  })
+  ,import_fengari_interop2.default.push(vm.L, result), 1
+) : 0;
 };
 function defineKeys(vm, numberOfKeys, commandArgs) {
   let keys2 = commandArgs.slice(0, numberOfKeys);

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions