| 16 | } |
| 17 | |
| 18 | const createRedisStub = () => { |
| 19 | const counters = new Map<string, number>() |
| 20 | const values = new Map<string, string>() |
| 21 | const sortedSets = new Map<string, StoredEnvelope[]>() |
| 22 | |
| 23 | const api = { |
| 24 | incr: vi.fn().mockImplementation((key: string) => { |
| 25 | const next = (counters.get(key) ?? 0) + 1 |
| 26 | counters.set(key, next) |
| 27 | return next |
| 28 | }), |
| 29 | expire: vi.fn().mockResolvedValue(1), |
| 30 | del: vi.fn().mockImplementation((...keys: string[]) => { |
| 31 | for (const key of keys) { |
| 32 | values.delete(key) |
| 33 | sortedSets.delete(key) |
| 34 | counters.delete(key) |
| 35 | } |
| 36 | return Promise.resolve(keys.length) |
| 37 | }), |
| 38 | zadd: vi.fn().mockImplementation((key: string, score: number, value: string) => { |
| 39 | const entries = sortedSets.get(key) ?? [] |
| 40 | entries.push({ score, value }) |
| 41 | sortedSets.set(key, entries) |
| 42 | return Promise.resolve(1) |
| 43 | }), |
| 44 | zremrangebyrank: vi.fn().mockImplementation((key: string, start: number, stop: number) => { |
| 45 | const entries = [...(sortedSets.get(key) ?? [])].sort((a, b) => a.score - b.score) |
| 46 | const normalizedStart = start < 0 ? Math.max(entries.length + start, 0) : start |
| 47 | const normalizedStop = stop < 0 ? entries.length + stop : stop |
| 48 | const next = entries.filter( |
| 49 | (_entry, index) => index < normalizedStart || index > normalizedStop |
| 50 | ) |
| 51 | sortedSets.set(key, next) |
| 52 | return Promise.resolve(1) |
| 53 | }), |
| 54 | zrangebyscore: vi.fn().mockImplementation((key: string, min: number, max: string) => { |
| 55 | const upperBound = max === '+inf' ? Number.POSITIVE_INFINITY : Number(max) |
| 56 | const entries = [...(sortedSets.get(key) ?? [])] |
| 57 | .filter((entry) => entry.score >= min && entry.score <= upperBound) |
| 58 | .sort((a, b) => a.score - b.score) |
| 59 | .map((entry) => entry.value) |
| 60 | return Promise.resolve(entries) |
| 61 | }), |
| 62 | set: vi.fn().mockImplementation((key: string, value: string) => { |
| 63 | values.set(key, value) |
| 64 | return Promise.resolve('OK') |
| 65 | }), |
| 66 | get: vi.fn().mockImplementation((key: string) => Promise.resolve(values.get(key) ?? null)), |
| 67 | pipeline: vi.fn().mockImplementation(() => { |
| 68 | const operations: Array<() => Promise<unknown>> = [] |
| 69 | const pipeline = { |
| 70 | zadd: (...args: [string, number, string]) => { |
| 71 | operations.push(() => api.zadd(...args)) |
| 72 | return pipeline |
| 73 | }, |
| 74 | expire: (...args: [string, number]) => { |
| 75 | operations.push(() => api.expire(...args)) |