Skip to content

Instantly share code, notes, and snippets.

@sobstel
Created November 29, 2023 12:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sobstel/24b8418f2bffd7019d88bf9653773dfa to your computer and use it in GitHub Desktop.
Save sobstel/24b8418f2bffd7019d88bf9653773dfa to your computer and use it in GitHub Desktop.
Partial node-redis mock
const now = () => new Date().getTime() / 1000;
let values = {};
let ttls = {}
const client = {
    del: async (key) => {
        delete values[key];
    },
    expire: async (key, ttl) => {
        ttls[key] = now + ttl;
    },
    get: async (commandOptions, key) => {
        return values[key] || null;
    },
    hGet: async (commandOptions, key, element) => {
        if (!(key in values && element in values[key])) {
            return null;
        }
        return values[key][element];
    },
    hGetAll: async (commandOptions, key) => {
        if (!(key in values)) {
            return [];
        }
        return values[key];
    },
    lRange: async (commandOptions, key, start, stop) => {
        if (!(key in values)) {
            return [];
        }
        return values[key].slice(start, (stop < 0 ? values[key].length + stop : stop) + 1);
    },
    mGet: async (commandOptions, keys) => {
        return keys.map(key => values[key] || null);
    },
    rPush: async (key, elements) => {
        values[key] ||= [];
        values[key].push(...elements);
        return values[key].length;
    },
    // partial implementation
    scan: async (commandOptions, cursor, scanOptions) => {
        // naive match assuming all our patterns end with wildcard "*" only
        const keys = Object.keys(values).reduce((acc, key) => {
            if (key.startsWith(scanOptions['MATCH'].slice(0, -1))) {
                acc.push(key);
            }
            return acc;
        }, []);
        return ["0", keys.slice(0, scanOptions['COUNT'])];
    },
    sendCommand: async ([command, ...args]) => {
        if (command === 'HMSET') {
            const [key, ...keyArgs] = args;
            values[key] ||= {}
            for (let i = 0; i < keyArgs.length; i += 2) {
                values[key][keyArgs[i]] = keyArgs[i+1];
            }
            return;
        }
        throw new Error('Unrecognized command');
    },
    set: async (key, value, setOptions = {}) => {
        if ('NX' in setOptions && key in values) {
            return;
        }
        if ('EX' in setOptions) {
            client.expire(key, setOptions['EX']);
        }
        values[key] = value;
    },
    ttl: async (key) => {
        if  (ttls[key]) {
             return Math.max(ttl[key] - now, 0);
        }
    },
    flushDb: async () => {
        values = {};
        ttls = {};
    }
}
export default () => client;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment