Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.21.0</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
20 changes: 19 additions & 1 deletion src/main/java/org/redis/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package org.redis;

import redis.clients.jedis.Jedis;

import java.util.Map;

public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
Map<String, String> map = new RedisMap(new Jedis("localhost", 6379));

map.putAll(Map.of(
"User 1", "123",
"User 2", "456",
"User 3", "789"
));

System.out.println("KeySet: " + map.keySet());
System.out.println("Values: " + map.values());
System.out.println("Size: " + map.size());

map.clear();
System.out.println("IsEmpty " + map.isEmpty());

}
}
128 changes: 128 additions & 0 deletions src/main/java/org/redis/RedisMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package org.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;

import java.util.*;

public class RedisMap implements Map<String, String> {

private final Jedis jedis;

public RedisMap(Jedis jedis) {
this.jedis = jedis;
}

@Override
public int size() {
return Math.toIntExact(jedis.dbSize());
}

@Override
public boolean isEmpty() {
return jedis.dbSize() == 0;
}

@Override
public boolean containsKey(Object key) {
if (key instanceof String keyStr) {
String value = jedis.get(keyStr);
return value != null;
} else {
throw new ClassCastException();
}
}

@Override
public boolean containsValue(Object value) {
if (value instanceof String valueStr) {
Collection<String> values = this.values();
for (String str : values) {
if (valueStr.equals(str)) return true;
}
return false;
}else {
throw new ClassCastException();
}
}

@Override
public String get(Object key) {
if (key instanceof String keyStr){
return jedis.get(keyStr);
} else {
throw new ClassCastException();
}
}

@Override
public String put(String key, String value) {
String previousValue = jedis.get(key);
jedis.set(key, value);
return previousValue;
}

@Override
public String remove(Object key) {
if (key instanceof String keyStr){
String previousValue = jedis.get(keyStr);
jedis.del(keyStr);
return previousValue;
} else {
throw new ClassCastException();
}
}

@Override
public void putAll(Map<? extends String, ? extends String> m) {
if (m.isEmpty()) return;
String[] keysAndValues = new String[m.size() * 2];
int i = 0;
for (Entry<? extends String, ? extends String> entry : m.entrySet()) {
keysAndValues[i++] = entry.getKey();
keysAndValues[i++] = entry.getValue();
}
jedis.mset(keysAndValues);
}

@Override
public void clear() {
jedis.flushDB();
}

@Override
public Set<String> keySet() {
return this.getKeySet();
}

@Override
public Collection<String> values() {
return jedis.mget(this.getKeySet().toArray(new String[0]));
}

@Override
public Set<Entry<String, String>> entrySet() {
Set<String> keySet = this.getKeySet();
String[] keysArray = keySet.toArray(new String[0]);
List<String> values = jedis.mget(keysArray);

Set<Entry<String, String>> entrySet = new HashSet<>(keySet.size());
for (int i = 0; i < keysArray.length; i++) {
entrySet.add(new AbstractMap.SimpleEntry<>(keysArray[i], values.get(i)));
}
return entrySet;
}

private Set<String> getKeySet() {
Set<String> keySet = new HashSet<>();
ScanParams scanParams = new ScanParams().count(500);
String cursor = ScanParams.SCAN_POINTER_START;
do {
ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
keySet.addAll(scanResult.getResult());
cursor = scanResult.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
return keySet;
}
}
200 changes: 200 additions & 0 deletions src/test/java/org/redis/RedisMapTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package org.redis;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;

import java.util.*;

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class RedisMapTest {

private Jedis jedisMock;
private RedisMap redisMap;

@BeforeEach
void setUp() {
this.jedisMock = mock(Jedis.class);
this.redisMap = new RedisMap(jedisMock);
}

@Test
void size() {
when(jedisMock.dbSize()).thenReturn(3L);
int size = redisMap.size();
verify(jedisMock).dbSize();
assertEquals(3, size);
}

@Test
void isEmpty1() {
when(jedisMock.dbSize()).thenReturn(3L);
boolean isEmpty = redisMap.isEmpty();
verify(jedisMock).dbSize();
assertFalse(isEmpty);
}

@Test
void isEmpty2() {
when(jedisMock.dbSize()).thenReturn(0L);
boolean isEmpty = redisMap.isEmpty();
verify(jedisMock).dbSize();
assertTrue(isEmpty);
}

@Test
void containsKey() {
when(jedisMock.get("key 1")).thenReturn("value 1");
boolean result = redisMap.containsKey("key 1");
verify(jedisMock).get("key 1");
assertTrue(result);
}

@Test
void containsValue() {
ScanResult<String> scanResult = mock(ScanResult.class);
when(scanResult.getResult()).thenReturn(List.of("key 1", "key 2", "key 3"));
when(scanResult.getCursor()).thenReturn("0");

when(jedisMock.scan(anyString(), any(ScanParams.class)))
.thenReturn(scanResult);

when(jedisMock.mget(any(String[].class)))
.thenReturn(List.of("value 1", "value 2", "value 3"));

boolean containsValue = redisMap.containsValue("value 2");
verify(jedisMock).scan(anyString(), any(ScanParams.class));

ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
verify(jedisMock).mget(argumentCaptor.capture());
String[] capturedKeys = argumentCaptor.getValue();
assertEquals(3, capturedKeys.length);
assertEquals(Set.of("key 1", "key 2", "key 3"), new HashSet<>(Arrays.asList(capturedKeys)));

assertTrue(containsValue);
}

@Test
void get() {
when(jedisMock.get("Key 1")).thenReturn("Value 1");
String result = redisMap.get("Key 1");
verify(jedisMock).get("Key 1");
assertEquals("Value 1", result);
}

@Test
void put1() {
when(jedisMock.get("Key 1")).thenReturn(null);
String result = redisMap.put("Key 1", "Value 1");
verify(jedisMock).get("Key 1");
assertEquals(null, result);
}

@Test
void put2() {
when(jedisMock.get("Key 1")).thenReturn("Value 1");
String result = redisMap.put("Key 1", "Value 2");
verify(jedisMock).get("Key 1");
assertEquals("Value 1", result);
}

@Test
void remove() {
when(jedisMock.get("Key 1")).thenReturn("Value 1");
String result = redisMap.remove("Key 1");
verify(jedisMock).del("Key 1");
assertEquals("Value 1", result);
}

@Test
void putAll() {
Map<String, String> input = Map.of("Key 1", "Value 1", "Key 2", "Value 2", "Key 3", "Value 3");
redisMap.putAll(input);

ArgumentCaptor<String[]> captor = ArgumentCaptor.forClass(String[].class);
verify(jedisMock).mset(captor.capture());
String[] kvArray = captor.getValue();

Map<String, String> actual = new HashMap<>();
for (int i = 0; i < kvArray.length; i += 2) {
actual.put(kvArray[i], kvArray[i + 1]);
}
assertEquals(input, actual);
}

@Test
void clear() {
redisMap.clear();
verify(jedisMock).flushDB();
}

@Test
void keySet() {
ScanResult<String> scanResult = mock(ScanResult.class);
when(scanResult.getResult()).thenReturn(List.of("key1", "key2", "key3"));
when(scanResult.getCursor()).thenReturn("0");

when(jedisMock.scan(anyString(), any(ScanParams.class)))
.thenReturn(scanResult);

Set<String> keySet = redisMap.keySet();

verify(jedisMock).scan(anyString(), any(ScanParams.class));
assertEquals(Set.of("key1", "key2", "key3"), keySet);
}

@Test
void values() {
ScanResult<String> scanResult = mock(ScanResult.class);
when(scanResult.getResult()).thenReturn(List.of("key1", "key2", "key3"));
when(scanResult.getCursor()).thenReturn("0");

when(jedisMock.scan(anyString(), any(ScanParams.class)))
.thenReturn(scanResult);

when(jedisMock.mget("key1", "key2", "key3"))
.thenReturn(List.of("value1", "value2", "value3"));

Collection<String> values = redisMap.values();
assertTrue(values.containsAll(List.of("value1", "value2", "value3")));

verify(jedisMock).scan(anyString(), any(ScanParams.class));

ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(jedisMock).mget(argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture());
List<String> capturedKeys = argumentCaptor.getAllValues();
assertTrue(capturedKeys.containsAll(List.of("key1", "key2", "key3")));
}

@Test
void entrySet() {
ScanResult<String> scanResult = mock(ScanResult.class);
when(scanResult.getResult()).thenReturn(List.of("key1", "key2", "key3"));
when(scanResult.getCursor()).thenReturn("0");

when(jedisMock.scan(anyString(), any(ScanParams.class)))
.thenReturn(scanResult);

when(jedisMock.mget("key1", "key2", "key3"))
.thenReturn(List.of("value1", "value2", "value3"));

Set<Map.Entry<String, String>> entries = redisMap.entrySet();

assertEquals(3, entries.size());
assertTrue(entries.contains(new AbstractMap.SimpleEntry<>("key1", "value1")));
assertTrue(entries.contains(new AbstractMap.SimpleEntry<>("key2", "value2")));
assertTrue(entries.contains(new AbstractMap.SimpleEntry<>("key3", "value3")));

verify(jedisMock).scan(anyString(), any(ScanParams.class));

ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(jedisMock).mget(argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture());
List<String> capturedKeys = argumentCaptor.getAllValues();
assertTrue(capturedKeys.containsAll(List.of("key1", "key2", "key3")));
}
}