-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(commands): add new command LMOVE (#1292)
Co-authored-by: Philip Nicholls <[email protected]>
- Loading branch information
1 parent
b3a6578
commit 7222ca8
Showing
3 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
export function lmove(listKey1, listKey2, position1, position2) { | ||
|
||
if (this.data.has(listKey1) && !(this.data.get(listKey1) instanceof Array)) { | ||
throw new Error(`Key ${listKey1} does not contain a list`) | ||
} | ||
if (this.data.has(listKey2) && !(this.data.get(listKey2) instanceof Array)) { | ||
throw new Error(`Key ${listKey2} does not contain a list`) | ||
} | ||
|
||
if (position1 !== "LEFT" && position1 !== "RIGHT") { | ||
throw new Error("Position1 argument must be 'LEFT' or 'RIGHT'"); | ||
} | ||
if (position2 !== "LEFT" && position2 !== "RIGHT") { | ||
throw new Error("Position2 argument must be 'LEFT' or 'RIGHT'"); | ||
} | ||
|
||
const list1 = this.data.get(listKey1) || [] | ||
let list2 = list1; // Operate on the same list | ||
if (listKey1 !== listKey2) { | ||
// Operate on two different lists | ||
list2 = this.data.get(listKey2) || [] | ||
} | ||
|
||
if (list1.length === 0) { | ||
return null; | ||
} | ||
|
||
let value; | ||
if (position1 === "LEFT") { | ||
value = list1.shift(); | ||
} else { | ||
value = list1.pop(); | ||
} | ||
|
||
if (position2 === "LEFT") { | ||
list2.unshift(value); | ||
} else { | ||
list2.push(value); | ||
} | ||
|
||
this.data.set(listKey1, list1); | ||
if (listKey2 !== listKey1) { | ||
this.data.set(listKey2, list2); | ||
} | ||
|
||
return value | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import Redis from 'ioredis' | ||
|
||
// eslint-disable-next-line import/no-relative-parent-imports | ||
import { runTwinSuite } from '../../../test-utils' | ||
|
||
runTwinSuite('lmove', command => { | ||
describe(command, () => { | ||
const redis = new Redis() | ||
afterAll(() => { | ||
redis.disconnect() | ||
}) | ||
|
||
const listId1 = "LIST1"; | ||
const listId2 = "LIST2"; | ||
const emptyList = "EMPTY"; | ||
const notalist = "NOTALIST"; | ||
|
||
beforeEach(async () => { | ||
await redis.del(listId1); | ||
await redis.del(listId2); | ||
await redis.del(notalist); | ||
|
||
await redis.lpush(emptyList, "TEST"); | ||
await redis.lpop(emptyList); | ||
const membersEmpty = await redis.lrange(listId1, 0, -1); | ||
expect(membersEmpty).toEqual([]); | ||
|
||
await redis.lpush(listId1, ['two', 'one']); | ||
await redis.lpush(listId2, ['four', 'three']); | ||
await redis.set(notalist, "TEST"); | ||
const members1 = await redis.lrange(listId1, 0, -1); | ||
const members2 = await redis.lrange(listId2, 0, -1); | ||
expect(members1).toEqual(['one', 'two']); | ||
expect(members2).toEqual(['three', 'four']); | ||
}); | ||
|
||
it('should move the value from LEFT of list1 to LEFT of list2', async () => { | ||
const result = await redis.lmove(listId1, listId2, "LEFT", "LEFT"); | ||
expect(result).toEqual('one'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['two']); | ||
expect(current2).toEqual(['one', 'three', 'four']); | ||
}); | ||
|
||
it('should move the value from RIGHT of list1 to LEFT of list2', async () => { | ||
const result = await redis.lmove(listId1, listId2, "RIGHT", "LEFT"); | ||
expect(result).toEqual('two'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one']); | ||
expect(current2).toEqual(['two', 'three', 'four']); | ||
}); | ||
|
||
it('should move the value from LEFT of list1 to RIGHT of list2', async () => { | ||
const result = await redis.lmove(listId1, listId2, "LEFT", "RIGHT"); | ||
expect(result).toEqual('one'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['two']); | ||
expect(current2).toEqual(['three', 'four', 'one']); | ||
}); | ||
|
||
it('should move the value from RIGHT of list1 to RIGHT of list2', async () => { | ||
const result = await redis.lmove(listId1, listId2, "RIGHT", "RIGHT"); | ||
expect(result).toEqual('two'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one']); | ||
expect(current2).toEqual(['three', 'four', 'two']); | ||
}); | ||
|
||
it('should rotate the list if the source and destination are the same', async () => { | ||
const result = await redis.lmove(listId2, listId2, "LEFT", "RIGHT"); | ||
expect(result).toEqual('three'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one', 'two']); | ||
expect(current2).toEqual(['four', 'three']); | ||
}); | ||
|
||
it('should perform no operation if the source is an empty list', async () => { | ||
const result = await redis.lmove(emptyList, listId2, "LEFT", "LEFT"); | ||
expect(result).toEqual(null); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one', 'two']); | ||
expect(current2).toEqual(['three', 'four']); | ||
}); | ||
|
||
it('should perform no operation if the source and destination are the same and both positions are LEFT', async () => { | ||
const result = await redis.lmove(listId2, listId2, "LEFT", "LEFT"); | ||
expect(result).toEqual('three'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one', 'two']); | ||
expect(current2).toEqual(['three', 'four']); | ||
}); | ||
|
||
it('should perform no operation if the source and destination are the same and both positions are RIGHT', async () => { | ||
const result = await redis.lmove(listId2, listId2, "RIGHT", "RIGHT"); | ||
expect(result).toEqual('four'); | ||
|
||
const current1 = await redis.lrange(listId1, 0, -1); | ||
const current2 = await redis.lrange(listId2, 0, -1); | ||
expect(current1).toEqual(['one', 'two']); | ||
expect(current2).toEqual(['three', 'four']); | ||
}); | ||
|
||
|
||
it('should perform no operation and return nil when source does not exist', async () => { | ||
const value = await redis.get("unknown"); | ||
expect(value).toEqual(null); // Ensures nil is being represented by null | ||
|
||
const result = await redis.lmove("unknown", listId2, "LEFT", "LEFT"); | ||
expect(result).toEqual(null); | ||
}); | ||
|
||
it('should error if the value is not a list', async () => { | ||
expect(async () => { | ||
await redis.lmove(notalist, listId2, "LEFT", "LEFT"); | ||
}).rejects.toThrow("Key NOTALIST does not contain a list"); | ||
}); | ||
}) | ||
}) |