diff --git a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java index f96a777..7d1e511 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java +++ b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java @@ -20,10 +20,7 @@ package net.lamgc.cgj.bot.cache.redis; import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.CacheStore; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @@ -44,7 +41,7 @@ public abstract class RedisCacheStore implements CacheStore { * @return 返回 Key 前缀. */ protected String getKeyString(CacheKey cacheKey) { - return RedisUtils.toRedisCacheKey(getKeyPrefix(), cacheKey); + return RedisUtils.toRedisCacheKey(getKeyPrefix(), Objects.requireNonNull(cacheKey)); } /** @@ -105,7 +102,7 @@ public abstract class RedisCacheStore implements CacheStore { final int prefixLength = getKeyPrefix().length(); Set newKeys = new HashSet<>(); for (String key : keys) { - newKeys.add(key.substring(prefixLength)); + newKeys.add(key.substring(prefixLength + 1)); } return newKeys; } diff --git a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStore.java b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStore.java index e42f209..734c67c 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStore.java +++ b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStore.java @@ -68,17 +68,6 @@ public class RedisListCacheStore extends RedisCacheStore> implements return result; } - /** - * {@inheritDoc} - * - *

注意: 在 Redis 实现中, 该功能通过一段 Lua 脚本实现, - * 由于 Redis 并没有原生支持该功能, 所以只能用脚本遍历查找. - * 如果 List 元素过多, 可能会导致执行缓慢且影响后续操作, 谨慎使用. - * @param key 待操作的缓存项键名. - * @param index 欲删除元素的索引, 从 0 开始. - * @return 如果元素存在且删除成功, 返回 true. - * @throws NullPointerException 当 key 为 null 时抛出. - */ @Override public boolean removeElement(CacheKey key, int index) { List keys = new ArrayList<>(1); @@ -98,8 +87,9 @@ public class RedisListCacheStore extends RedisCacheStore> implements @Override public boolean addElement(CacheKey key, E element) { Objects.requireNonNull(element); - return connectionPool.executeRedis(jedis -> + connectionPool.executeRedis(jedis -> jedis.lpush(getKeyString(key), converter.to(element)) != RedisUtils.RETURN_CODE_FAILED); + return true; } @Override @@ -115,14 +105,15 @@ public class RedisListCacheStore extends RedisCacheStore> implements valueStrings[i] = converter.to(values.get(i)); } - return connectionPool.executeRedis(jedis -> + connectionPool.executeRedis(jedis -> jedis.lpush(getKeyString(key), valueStrings) != RedisUtils.RETURN_CODE_FAILED); + return true; } /** * {@inheritDoc} * - *

注意: 在 Redis 实现中, 该功能通过一段 Lua 脚本实现, + *

注意: 在 Redis 实现中, 该功能通过一段 Lua 脚本实现, * 由于 Redis 并没有原生支持该功能, 所以只能用脚本遍历查找. * 如果 List 元素过多, 可能会导致执行缓慢且影响后续操作, 谨慎使用. * @param key 待检查的缓存项键名. diff --git a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStore.java b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStore.java index 1bbd56e..00c447e 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStore.java +++ b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStore.java @@ -17,7 +17,6 @@ package net.lamgc.cgj.bot.cache.redis; -import com.google.common.base.Strings; import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.MapCacheStore; import net.lamgc.cgj.bot.cache.convert.StringConverter; @@ -38,8 +37,11 @@ public class RedisMapCacheStore extends RedisCacheStore> imple public RedisMapCacheStore(RedisConnectionPool connectionPool, String keyPrefix, StringConverter converter) { super(connectionPool); this.connectionPool = connectionPool; - keyPrefix = Strings.nullToEmpty(keyPrefix).trim(); - if (!keyPrefix.isEmpty() && keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) { + keyPrefix = Objects.requireNonNull(keyPrefix).trim(); + if (keyPrefix.isEmpty()) { + throw new IllegalArgumentException("Key prefix cannot be empty."); + } + if (keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) { this.keyPrefix = keyPrefix; } else { this.keyPrefix = keyPrefix + RedisUtils.KEY_SEPARATOR; @@ -100,10 +102,11 @@ public class RedisMapCacheStore extends RedisCacheStore> imple public boolean put(CacheKey key, String field, V value) { Objects.requireNonNull(field); Objects.requireNonNull(value); - return connectionPool.executeRedis(jedis -> { + connectionPool.executeRedis(jedis -> { String keyString = getKeyString(key); - return jedis.hset(keyString, field, converter.to(value)) == RedisUtils.RETURN_CODE_OK; + return jedis.hset(keyString, field, converter.to(value)); }); + return true; } @Override @@ -162,17 +165,7 @@ public class RedisMapCacheStore extends RedisCacheStore> imple @Override public boolean clearMap(CacheKey key) { - Set fields = mapFieldSet(key); - if (fields == null) { - return false; - } else if (fields.size() == 0) { - return true; - } - - String[] fieldsArray = new String[fields.size()]; - fields.toArray(fieldsArray); - return connectionPool.executeRedis(jedis -> - jedis.hdel(getKeyString(key), fieldsArray) != RedisUtils.RETURN_CODE_FAILED); + return remove(key); } } diff --git a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStore.java b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStore.java index e01ddf0..6baf73a 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStore.java +++ b/ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStore.java @@ -17,7 +17,6 @@ package net.lamgc.cgj.bot.cache.redis; -import com.google.common.base.Strings; import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.SingleCacheStore; import net.lamgc.cgj.bot.cache.convert.StringConverter; @@ -41,8 +40,11 @@ public class RedisSingleCacheStore extends RedisCacheStore implements Sing public RedisSingleCacheStore(RedisConnectionPool connectionPool, String keyPrefix, StringConverter converter) { super(connectionPool); this.connectionPool = connectionPool; - keyPrefix = Strings.nullToEmpty(keyPrefix).trim(); - if (!keyPrefix.isEmpty() && keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) { + keyPrefix = Objects.requireNonNull(keyPrefix).trim(); + if (keyPrefix.isEmpty()) { + throw new IllegalArgumentException("Key prefix cannot be empty."); + } + if (keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) { this.keyPrefix = keyPrefix; } else { this.keyPrefix = keyPrefix + RedisUtils.KEY_SEPARATOR; diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStoreTest.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStoreTest.java new file mode 100644 index 0000000..e38cd14 --- /dev/null +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStoreTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2021 LamGC + * + * ContentGrabbingJi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License. + * + * ContentGrabbingJi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package net.lamgc.cgj.bot.cache.redis; + +import net.lamgc.cgj.bot.cache.CacheKey; +import org.junit.*; +import redis.clients.jedis.Jedis; + +import java.util.HashSet; +import java.util.Set; + +import static net.lamgc.cgj.bot.cache.redis.util.RedisTestUtils.assertDeleteIfExist; +import static net.lamgc.cgj.bot.cache.redis.util.RedisTestUtils.randomString; + +/** + * @see RedisCacheStore + */ +public class RedisCacheStoreTest { + + private final static String KEY_PREFIX = "test:store"; + + private static RedisConnectionPool connectionPool; + private static RedisCacheStore cacheStore; + + private static Jedis jedis; + + @BeforeClass + public static void beforeAllTest() { + jedis = new Jedis(); + connectionPool = new RedisConnectionPool(); + cacheStore = new SimpleRedisCacheStore(connectionPool, KEY_PREFIX); + + } + + @AfterClass + public static void afterAllTest() { + if (jedis != null && jedis.isConnected()) { + jedis.close(); + } + + connectionPool.close(); + } + + @Before + public void beforeTest() { + clearAllKey(); + } + + @After + public void afterTest() { + clearAllKey(); + } + + private void clearAllKey() { + Set keys = jedis.keys(RedisUtils.toRedisCacheKey(KEY_PREFIX, RedisUtils.CACHE_KEY_ALL)); + if (keys.isEmpty()) { + return; + } + String[] keyArray = new String[keys.size()]; + keys.toArray(keyArray); + Assert.assertEquals("Failed to delete test key.", + keyArray.length, jedis.del(keyArray).intValue()); + } + + @Test + public void setTimeToLiveTest() { + final CacheKey key = new CacheKey("test_set_time_to_live"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final long timeToLive = 500; + + assertDeleteIfExist(jedis, keyString); + Assert.assertFalse("TTL set successfully for nonexistent key.", cacheStore.setTimeToLive(key, timeToLive)); + Assert.assertTrue("Failed to set key value for test.", RedisUtils.isOk(jedis.set(keyString, "value"))); + + Assert.assertTrue("Failed to set TTL on key value.", cacheStore.setTimeToLive(key, timeToLive)); + long currentTTL = jedis.ttl(keyString); + Assert.assertTrue("TTL was not set successfully.", currentTTL >= 0); + Assert.assertTrue("The actual set TTL is greater than expected.", + currentTTL < timeToLive); + + Assert.assertTrue("Failed to clear TTL on key value.", cacheStore.setTimeToLive(key, -1)); + currentTTL = jedis.ttl(keyString); + Assert.assertTrue("TTL was not clear successfully.", currentTTL < 0); + } + + @Test + public void getTimeToLiveTest() { + final CacheKey key = new CacheKey("test_get_time_to_live"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final long timeToLive = 500; + + assertDeleteIfExist(jedis, keyString); + Assert.assertEquals("The getTimeToLive() method returned a positive integer for a nonexistent key.", + -1, cacheStore.getTimeToLive(key)); + Assert.assertTrue("Failed to set key value for test.", RedisUtils.isOk(jedis.set(keyString, "value"))); + + Assert.assertEquals("Failed to set TTL on key value.", 1, jedis.pexpire(keyString, timeToLive).intValue()); + long currentTTL = cacheStore.getTimeToLive(key); + Assert.assertTrue("TTL failed to get.", currentTTL > 0); + Assert.assertTrue("The actual set TTL is greater than expected.", + currentTTL <= timeToLive); + Assert.assertTrue("The actual set TTL is greater than expected.", + currentTTL >= (timeToLive - 5)); + + Assert.assertEquals("Failed to clear TTL on key value.", + 1, jedis.persist(keyString).intValue()); + currentTTL = cacheStore.getTimeToLive(key); + Assert.assertTrue("TTL was not clear successfully.", currentTTL < 0); + } + + @Test + public void sizeTest() { + final String keyPrefix = + RedisUtils.toRedisCacheKey(KEY_PREFIX, new CacheKey("test_size")) + ':'; + final String allKey = RedisUtils.toRedisCacheKey(keyPrefix, RedisUtils.CACHE_KEY_ALL); + + for (int i = 0; i < 50; i++) { + Assert.assertTrue("Failed to add key.", RedisUtils.isOk( + jedis.set( + RedisUtils.toRedisCacheKey(keyPrefix, new CacheKey(randomString(8))), + randomString(10) + ))); + + Assert.assertEquals("Inconsistent number of keys.", jedis.keys(allKey).size(), cacheStore.size()); + } + } + + @Test + public void existsTest() { + final CacheKey key = new CacheKey("test_exists"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertFalse("The exists() method returned 'true' for a nonexistent key.", cacheStore.exists(key)); + Assert.assertTrue("Failed to set key value for test.", RedisUtils.isOk(jedis.set(keyString, "value"))); + Assert.assertTrue("The exists() method returned 'false' for the existing key.", cacheStore.exists(key)); + } + + @Test + public void removeTest() { + final CacheKey key = new CacheKey("test_remove"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertFalse("Attempt to delete nonexistent key succeeded.", cacheStore.remove(key)); + Assert.assertTrue("Failed to set key value for test.", RedisUtils.isOk(jedis.set(keyString, "value"))); + Assert.assertTrue("Key deletion failed.", cacheStore.remove(key)); + } + + @Test + public void clearTest() { + final CacheKey key = new CacheKey("test_clear"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + final String externalKey = "test:external"; + + Assert.assertTrue("Failed to set external key.", RedisUtils.isOk(jedis.set(externalKey, "externalValue"))); + Assert.assertTrue("Failed to set test key.", RedisUtils.isOk(jedis.set(keyString, "testValue"))); + Assert.assertNotEquals("The cache store is empty.", + 0, jedis.keys(RedisUtils.toRedisCacheKey(KEY_PREFIX, RedisUtils.CACHE_KEY_ALL)).size()); + + Assert.assertTrue(cacheStore.clear()); + + Assert.assertEquals("The cache store is not empty.", + 0, jedis.keys(RedisUtils.toRedisCacheKey(KEY_PREFIX, RedisUtils.CACHE_KEY_ALL)).size()); + Assert.assertTrue("External key removed.", jedis.exists(externalKey)); + } + + @Test + public void keySetTest() { + clearAllKey(); + Set actualKeys = cacheStore.keySet(); + Assert.assertNotNull("keySet() returned 'null'.", actualKeys); + Assert.assertEquals("CacheStore is empty, but keySet() is not empty.", 0, actualKeys.size()); + + Set expectKeys = new HashSet<>(); + for (int i = 0; i < 50; i++) { + String nextKey = randomString(10); + expectKeys.add(nextKey); + Assert.assertTrue("Failed to set key value for test.", + RedisUtils.isOk(jedis.set(KEY_PREFIX + RedisUtils.KEY_SEPARATOR + nextKey, nextKey))); + } + + actualKeys = cacheStore.keySet(); + Assert.assertNotNull("keySet() returned 'null'.", actualKeys); + Assert.assertEquals("Number of keys not as expected.", expectKeys.size(), actualKeys.size()); + + for (String actualKey : actualKeys) { + Assert.assertTrue(String.format("Key '%s' does not belong to the expected key.", actualKey), + expectKeys.contains(actualKey)); + } + } + +} diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStoreTest.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStoreTest.java index 3a72ad0..0746fa6 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStoreTest.java +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisListCacheStoreTest.java @@ -21,10 +21,7 @@ import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.ListCacheStore; import net.lamgc.cgj.bot.cache.convert.StringConverter; import net.lamgc.cgj.bot.cache.convert.StringToStringConverter; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; import redis.clients.jedis.Jedis; import java.lang.reflect.Field; @@ -35,18 +32,26 @@ import java.util.*; */ public class RedisListCacheStoreTest { - private final Jedis jedis = new Jedis(); + private static Jedis jedis; private final static StringConverter CONVERTER = new StringToStringConverter(); private final static String IDENTIFY = "test:list"; private static RedisConnectionPool connectionPool; @BeforeClass public static void beforeAllTest() { + jedis = new Jedis(); connectionPool = new RedisConnectionPool(); connectionPool.reconnectRedis(); Assert.assertTrue("Redis is not connected.", connectionPool.available()); } + @AfterClass + public static void afterAllTest() { + if (jedis != null) { + jedis.close(); + } + } + private ListCacheStore newListCacheStore() { return new RedisListCacheStore<>(connectionPool, IDENTIFY, CONVERTER); } @@ -72,10 +77,12 @@ public class RedisListCacheStoreTest { final Field prefixField = RedisListCacheStore.class.getDeclaredField("keyPrefix"); prefixField.setAccessible(true); String prefix = (String) prefixField.get(new RedisListCacheStore<>(connectionPool, IDENTIFY, CONVERTER)); - Assert.assertTrue(prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + Assert.assertTrue("The prefix does not contain a separator at the end.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); prefix = (String) prefixField.get(new RedisListCacheStore<>(connectionPool, IDENTIFY + RedisUtils.KEY_SEPARATOR, CONVERTER)); - Assert.assertTrue(prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + Assert.assertTrue("The separator at the end of the prefix is missing.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); prefixField.setAccessible(false); } @@ -95,10 +102,14 @@ public class RedisListCacheStoreTest { final CacheKey listKey = new CacheKey("list_add_element"); final String element = "test"; final String listKeyStr = RedisUtils.toRedisCacheKey(IDENTIFY, listKey); - Assert.assertTrue(listCacheStore.addElement(listKey, element)); + final int lastCount = jedis.llen(listKeyStr).intValue(); + Assert.assertTrue("The operation to be tested failed.", + listCacheStore.addElement(listKey, element)); - Assert.assertEquals(1, jedis.llen(listKeyStr).intValue()); - Assert.assertEquals(element, jedis.lpop(listKeyStr)); + Assert.assertEquals("The key value has no change to the quantity.", + lastCount + 1, jedis.llen(listKeyStr).intValue()); + Assert.assertEquals("The value of pop is different from that of push.", + element, jedis.lpop(listKeyStr)); } @Test @@ -117,12 +128,14 @@ public class RedisListCacheStoreTest { jedis.exists(listKeyStr), listCacheStore.addElements(listKey, Collections.emptyList())); + Assert.assertTrue("The operation to be tested failed.", + listCacheStore.addElements(listKey, expectedElements)); - Assert.assertTrue(listCacheStore.addElements(listKey, expectedElements)); - - Assert.assertEquals(expectedElements.size(), jedis.llen(listKeyStr).intValue()); + Assert.assertEquals("The quantity is not as expected.", + expectedElements.size(), jedis.llen(listKeyStr).intValue()); Set actualElements = getListElements(listKeyStr); - Assert.assertTrue(actualElements.containsAll(expectedElements)); + Assert.assertTrue("The added value is not as expected.", + actualElements.containsAll(expectedElements)); Assert.assertEquals("Key does not exist, but adding empty collection failed.", jedis.exists(listKeyStr), @@ -146,7 +159,8 @@ public class RedisListCacheStoreTest { jedis.del(listKeyStr); - Assert.assertFalse(listCacheStore.removeElement(listKey, "NoExistElement")); + Assert.assertFalse("The operation to be tested failed.", + listCacheStore.removeElement(listKey, "NoExistElement")); Assert.assertNotEquals("The expected create operation failed.", RedisUtils.RETURN_CODE_FAILED, jedis.lpush(listKeyStr, expectedElementsArr).intValue()); @@ -158,9 +172,11 @@ public class RedisListCacheStoreTest { expectedElements.remove(deletedIndex); - Assert.assertEquals(expectedElements.size(), jedis.llen(listKeyStr).intValue()); + Assert.assertEquals("The quantity is not as expected.", + expectedElements.size(), jedis.llen(listKeyStr).intValue()); Set actualElements = getListElements(listKeyStr); - Assert.assertTrue(actualElements.containsAll(expectedElements)); + Assert.assertTrue("The added value is not as expected.", + actualElements.containsAll(expectedElements)); } @Test @@ -180,10 +196,11 @@ public class RedisListCacheStoreTest { jedis.del(listKeyStr); // 尝试删除不存在的 Key - Assert.assertFalse(listCacheStore.removeElement(listKey, 0)); + Assert.assertFalse("The attempt to delete a value that does not exist succeeded.", + listCacheStore.removeElement(listKey, 0)); Assert.assertNotEquals("The expected create operation failed.", RedisUtils.RETURN_CODE_FAILED, jedis.lpush(listKeyStr, expectedElementsArr).intValue()); - Assert.assertFalse( + Assert.assertFalse("The attempt to delete the value of the illegal index succeeded.", listCacheStore.removeElement(listKey, jedis.llen(listKeyStr).intValue())); Random random = new Random(); @@ -195,9 +212,11 @@ public class RedisListCacheStoreTest { expectedElements.remove(deletedElement); - Assert.assertEquals(expectedElements.size(), jedis.llen(listKeyStr).intValue()); + Assert.assertEquals("The quantity is not as expected.", + expectedElements.size(), jedis.llen(listKeyStr).intValue()); Set actualElements = getListElements(listKeyStr); - Assert.assertTrue(actualElements.containsAll(expectedElements)); + Assert.assertTrue("The added value is not as expected.", + actualElements.containsAll(expectedElements)); } @Test @@ -215,7 +234,7 @@ public class RedisListCacheStoreTest { final String[] expectedElementsArr = new String[expectedElements.size()]; expectedElements.toArray(expectedElementsArr); - Assert.assertNotEquals(-1, jedis.lpush(listKeyStr, expectedElementsArr).intValue()); + jedis.lpush(listKeyStr, expectedElementsArr); Set actualElements = getListElements(listKeyStr); expectedElements.add("f"); @@ -224,7 +243,7 @@ public class RedisListCacheStoreTest { expectedElements.add("i"); for (String expectedElement : expectedElements) { - Assert.assertEquals(String.format("Make a difference: '%s'", expectedElement), + Assert.assertEquals(String.format("Make a difference: '%s'.", expectedElement), actualElements.contains(expectedElement), listCacheStore.containsElement(listKey, expectedElement)); } @@ -236,9 +255,11 @@ public class RedisListCacheStoreTest { final CacheKey listKey = new CacheKey("list_is_empty"); final String listKeyStr = RedisUtils.toRedisCacheKey(IDENTIFY, listKey); - Assert.assertEquals(!jedis.exists(listKeyStr), listCacheStore.isEmpty(listKey)); + Assert.assertEquals("Key does not exist but returns 'true'", + !jedis.exists(listKeyStr), listCacheStore.isEmpty(listKey)); jedis.lpush(listKeyStr, "test"); - Assert.assertEquals(jedis.exists(listKeyStr), !listCacheStore.isEmpty(listKey)); + Assert.assertEquals("Key does exist but returns 'false'.", + jedis.exists(listKeyStr), !listCacheStore.isEmpty(listKey)); } @Test @@ -257,15 +278,18 @@ public class RedisListCacheStoreTest { long beforeLength = jedis.llen(listKeyStr); if (jedis.llen(listKeyStr) == 0) { - Assert.assertEquals(-1, listCacheStore.elementsLength(listKey)); + Assert.assertEquals("The list was empty but returned a value other than '-1' or '0'.", + -1, listCacheStore.elementsLength(listKey)); } else { - Assert.assertEquals(beforeLength, listCacheStore.elementsLength(listKey)); + Assert.assertEquals("The length of the list is incorrect.", + beforeLength, listCacheStore.elementsLength(listKey)); } jedis.del(listKeyStr); jedis.lpush(listKeyStr, expectedElementsArr); - Assert.assertEquals(jedis.llen(listKeyStr).intValue(), listCacheStore.elementsLength(listKey)); + Assert.assertEquals("The length of the list is incorrect.", + jedis.llen(listKeyStr).intValue(), listCacheStore.elementsLength(listKey)); } @Test @@ -282,13 +306,16 @@ public class RedisListCacheStoreTest { final String[] expectedElementsArr = new String[expectedElements.size()]; expectedElements.toArray(expectedElementsArr); - Assert.assertEquals(jedis.exists(listKeyStr), listCacheStore.clearCollection(listKey)); + Assert.assertEquals("The list does not exist but returns 'true'.", + jedis.exists(listKeyStr), listCacheStore.clearCollection(listKey)); jedis.lpush(listKeyStr, expectedElementsArr); - Assert.assertTrue(listCacheStore.clearCollection(listKey)); - Assert.assertEquals(0, jedis.llen(listKeyStr).intValue()); - Assert.assertFalse(jedis.exists(listKeyStr)); + Assert.assertTrue("The operation to be tested failed.", + listCacheStore.clearCollection(listKey)); + Assert.assertEquals("The list is not empty after the clear operation", + 0, jedis.llen(listKeyStr).intValue()); + Assert.assertFalse("The list is still exist", jedis.exists(listKeyStr)); } @Test @@ -308,7 +335,7 @@ public class RedisListCacheStoreTest { Collections.reverse(expectedElements); for (int i = 0; i < expectedElements.size(); i++) { - Assert.assertEquals("index: " + i, expectedElements.get(i), + Assert.assertEquals("Wrong value, index: " + i, expectedElements.get(i), listCacheStore.getElement(listKey, i)); } } @@ -342,7 +369,8 @@ public class RedisListCacheStoreTest { List actualElements = listCacheStore.getElementsByRange(listKey, start, length); for (int i = 0; i < length; i++) { - Assert.assertEquals(expectedElements.get(start + i), actualElements.get(i)); + Assert.assertEquals("Wrong value, index: " + i, + expectedElements.get(start + i), actualElements.get(i)); } } diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStoreTest.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStoreTest.java index 1fe4ad2..55d3c07 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStoreTest.java +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisMapCacheStoreTest.java @@ -21,55 +21,100 @@ package net.lamgc.cgj.bot.cache.redis; import com.google.common.base.Throwables; import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.MapCacheStore; +import net.lamgc.cgj.bot.cache.convert.StringConverter; import net.lamgc.cgj.bot.cache.convert.StringToStringConverter; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.junit.rules.TemporaryFolder; +import redis.clients.jedis.Jedis; import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; + +import static net.lamgc.cgj.bot.cache.redis.util.RedisTestUtils.assertDeleteIfExist; +import static net.lamgc.cgj.bot.cache.redis.util.RedisTestUtils.randomString; /** * @see RedisMapCacheStore */ public class RedisMapCacheStoreTest { - private final static RedisCacheStoreFactory factory; private final static TemporaryFolder tempFolder = TemporaryFolder.builder().build(); + private final static String KEY_PREFIX = "test:map"; + private final static StringConverter CONVERTER = new StringToStringConverter(); + private final static RedisCacheStoreFactory factory = new RedisCacheStoreFactory(); - static { + private static MapCacheStore cacheStore; + + private static Jedis jedis; + + @BeforeClass + public static void beforeAll() { + jedis = new Jedis(); try { tempFolder.create(); } catch (IOException e) { Assert.fail(Throwables.getStackTraceAsString(e)); + return; } - factory = new RedisCacheStoreFactory(); + try { - factory.initial(tempFolder.newFolder("cache-redis")); + factory.initial(tempFolder.newFolder("Redis")); + cacheStore = + factory.newMapCacheStore(KEY_PREFIX, CONVERTER); } catch (IOException e) { Assert.fail(Throwables.getStackTraceAsString(e)); } } - private final static MapCacheStore cacheStore = factory.newMapCacheStore("test:map", new StringToStringConverter()); + @AfterClass + public static void afterAll() { + if (jedis != null) { + jedis.close(); + } + } @Before public void before() { - Assert.assertTrue(cacheStore.clear()); + Assert.assertTrue("Clear execution failed before the test started.", cacheStore.clear()); } @After public void after() { - Assert.assertTrue(cacheStore.clear()); + Assert.assertTrue("After the test, clear execution failed", cacheStore.clear()); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyPrefixTest() { + factory.newMapCacheStore("", CONVERTER); + } + + @Test(expected = NullPointerException.class) + public void nullPrefixTest() { + factory.newMapCacheStore(null, CONVERTER); + } + + @Test + public void prefixCheck() throws NoSuchFieldException, IllegalAccessException { + final Field prefixField = RedisMapCacheStore.class.getDeclaredField("keyPrefix"); + prefixField.setAccessible(true); + String prefix = (String) prefixField.get(factory.newMapCacheStore(KEY_PREFIX, CONVERTER)); + Assert.assertTrue("The prefix does not contain a separator at the end.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + prefix = (String) prefixField.get(factory.newMapCacheStore( + KEY_PREFIX + RedisUtils.KEY_SEPARATOR, CONVERTER)); + Assert.assertTrue("The separator at the end of the prefix is missing.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + prefixField.setAccessible(false); } @Test public void nullThrowTest() { - final CacheKey key = new CacheKey("testKey"); - + final CacheKey key = new CacheKey("test_null_throw"); + Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapSize(null)); Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapFieldSet(null)); Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapValueSet(null)); @@ -90,86 +135,280 @@ public class RedisMapCacheStoreTest { } @Test - public void keyNotExistTest() { - final CacheKey key = new CacheKey("testKey"); - HashMap testMap = new HashMap<>(); - testMap.put("testField", "value"); + public void putTest() { + final CacheKey key = new CacheKey("test_put"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field01"; + final String value = "value01"; - // 例外情况: 因为 Redis 对空 Map 的处理机制, 导致返回只能为空. - Assert.assertEquals(0, cacheStore.mapSize(key)); - // 例外情况: mapIsEmpty 在 Redis 没有相应指令, 所以是依靠 mapSize 实现的, - // 同样因 mapSize 的原因, 不存在等于空. - Assert.assertTrue(cacheStore.mapIsEmpty(key)); - - Assert.assertFalse(cacheStore.clearMap(key)); - Assert.assertFalse(cacheStore.containsField(key, "Field")); - Assert.assertFalse(cacheStore.removeField(key, "Field")); - Assert.assertNull(cacheStore.get(key, "Field")); - Assert.assertTrue(cacheStore.put(key, "Field", "value")); - Assert.assertTrue("clearMap operation failed!", cacheStore.remove(key)); - Assert.assertTrue(cacheStore.putAll(key, testMap)); - Assert.assertTrue("clearMap operation failed!", cacheStore.remove(key)); - Assert.assertTrue(cacheStore.putIfNotExist(key, "Field", "value")); + if (jedis.hexists(keyString, field)) { + Assert.assertEquals("The field used by the test is occupied and deletion failed.", + 1, jedis.hdel(keyString, field).intValue()); + } + Assert.assertTrue("The operation to be tested failed.", cacheStore.put(key, field, value)); + Assert.assertTrue("The field does not exist after put.", jedis.hexists(keyString, field)); + Assert.assertEquals("The value of the field changes after put.", value, jedis.hget(keyString, field)); } @Test - public void putAndGetTest() { - final CacheKey key = new CacheKey("testKey"); - final Map expectedMap = new HashMap<>(); - expectedMap.put("test01", "testValue01"); - expectedMap.put("test02", "testValue02"); - expectedMap.put("test03", "testValue03"); - expectedMap.put("test04", "testValue04"); - expectedMap.put("test05", "testValue05"); - expectedMap.put("test06", "testValue06"); + public void getTest() { + final CacheKey key = new CacheKey("test_get"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field01"; + final String value = "value01"; - // put/get, mapIsEmpty, containsField - Assert.assertTrue("put operation failed!", cacheStore.put(key, "test00", "testValue00")); - Assert.assertTrue(cacheStore.containsField(key, "test00")); - Assert.assertEquals("testValue00", cacheStore.get(key, "test00")); - Assert.assertTrue("removeField operation failed!", cacheStore.removeField(key, "test00")); + assertDeleteIfExist(jedis, keyString); + Assert.assertNull("Method returned a non null value for a field that does not exist.", + cacheStore.get(key, field)); - // putIfNotExist - Assert.assertTrue(cacheStore.putIfNotExist(key, "test00", "testValue00")); - Assert.assertFalse(cacheStore.putIfNotExist(key, "test00", "testValue00")); - Assert.assertTrue("clearMap operation failed!", cacheStore.clearMap(key)); + Assert.assertTrue("Failed to set the field used by the test.", + jedis.hset(keyString, field, value).intValue() >= 0); + Assert.assertTrue("The field used by the test does not exist.", + jedis.hexists(keyString, field)); - // putAll - // empty map, key no exist - Assert.assertFalse(cacheStore.putAll(key, new HashMap<>())); - // non-empty map - Assert.assertTrue(cacheStore.putAll(key, expectedMap)); - Assert.assertTrue(expectedMap.keySet().containsAll(cacheStore.mapFieldSet(key))); - Assert.assertTrue(expectedMap.values().containsAll(cacheStore.mapValueSet(key))); - // empty map, key exist - Assert.assertTrue(cacheStore.putAll(key, new HashMap<>())); + Assert.assertEquals("The obtained field value does not match the expected value.", + value, cacheStore.get(key, field)); } @Test - public void fieldChangeTest() { - final CacheKey key = new CacheKey("testKey"); - final Map expectedMap = new HashMap<>(); - expectedMap.put("test01", "testValue01"); - expectedMap.put("test02", "testValue02"); - expectedMap.put("test03", "testValue03"); - expectedMap.put("test04", "testValue04"); - expectedMap.put("test05", "testValue05"); - expectedMap.put("test06", "testValue06"); + public void putAllTest() { + final CacheKey key = new CacheKey("test_put_all"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); - // mapSize, clearMap, mapIsEmpty 测试 - Assert.assertTrue("putAll operation failed!", cacheStore.putAll(key, expectedMap)); - Assert.assertEquals(expectedMap.size(), cacheStore.mapSize(key)); - Assert.assertTrue(cacheStore.clearMap(key)); - Assert.assertEquals(0, cacheStore.mapSize(key)); - Assert.assertTrue(cacheStore.mapIsEmpty(key)); + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); - // removeField 多分支测试 - Assert.assertTrue("put operation failed!", cacheStore.put(key, "test00", "testValue00")); - Assert.assertTrue(cacheStore.containsField(key, "test00")); - Assert.assertEquals("testValue00", cacheStore.get(key, "test00")); - Assert.assertTrue("removeField operation failed!", cacheStore.removeField(key, "test00")); - Assert.assertFalse(cacheStore.removeField(key, "test00")); + assertDeleteIfExist(jedis, keyString); + + Assert.assertEquals("The key does not exist, but the empty collection was added successfully.", + jedis.exists(keyString), + cacheStore.putAll(key, Collections.emptyMap())); + + Assert.assertTrue("The operation to be tested failed.", cacheStore.putAll(key, expectMap)); + + Map actualMap = jedis.hgetAll(keyString); + for (String actualKey : actualMap.keySet()) { + Assert.assertTrue("Field does not exist in the expected.", expectMap.containsKey(actualKey)); + Assert.assertEquals("The value of field " + actualKey + " does not match the expected value.", + expectMap.get(actualKey), actualMap.get(actualKey)); + } + + Assert.assertEquals("Key does not exist, but adding empty collection failed.", + jedis.exists(keyString), + cacheStore.putAll(key, Collections.emptyMap())); } + @Test + public void putIfNotExistTest() { + final CacheKey key = new CacheKey("test_put_if_not_exist"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field01"; + final String value = "value01"; + + if (jedis.hexists(keyString, field)) { + Assert.assertEquals("The field used by the test is occupied and deletion failed.", + 1, jedis.hdel(keyString, field).intValue()); + } + + Assert.assertTrue("Field does not exist but put failed.", cacheStore.putIfNotExist(key, field, value)); + + Assert.assertFalse("Field does exist but put successful.", cacheStore.putIfNotExist(key, field, value)); + } + + @Test + public void mapSizeTest() { + final CacheKey key = new CacheKey("test_map_size"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertEquals("Non 0 returned when map does not exist.", 0, cacheStore.mapSize(key)); + Assert.assertEquals("The number of test fields prepared is inconsistent.", + expectMap.size(), jedis.hset(keyString, expectMap).intValue()); + + Assert.assertEquals("The number of fields obtained does not match the actual number.", + expectMap.size(), cacheStore.mapSize(key)); + } + + @Test + public void mapIsEmptyTest() { + final CacheKey key = new CacheKey("test_put_if_not_exist"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field01"; + final String value = "value01"; + + if (jedis.hlen(keyString) != 0) { + Assert.assertFalse("Map exists but returns 'true'.", cacheStore.mapIsEmpty(key)); + Assert.assertEquals("Failed to delete map.", 1, jedis.del(keyString).intValue()); + Assert.assertTrue("Map not exists but returns 'false'.", cacheStore.mapIsEmpty(key)); + } else { + Assert.assertTrue("Map not exists but returns 'false'.", cacheStore.mapIsEmpty(key)); + Assert.assertEquals("Failed to set field value for test.", + 1, jedis.hset(keyString, field, value).intValue()); + Assert.assertFalse("Map exists but returns 'true'.", cacheStore.mapIsEmpty(key)); + } + } + + @Test + public void mapFieldSetTest() { + final CacheKey key = new CacheKey("test_map_field_set"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertNull("The mapFieldSet() method returned a non null value for a nonexistent map." + + "(If the map does not exist, null should be returned instead of an empty set)", + cacheStore.mapFieldSet(key)); + + Assert.assertEquals("The number of test fields prepared is inconsistent.", + expectMap.size(), jedis.hset(keyString, expectMap).intValue()); + + Set fieldSet = cacheStore.mapFieldSet(key); + Assert.assertNotNull("Method returns 'null' for the existing map.", fieldSet); + + Assert.assertTrue("The actual set is different from the expectation.", + fieldSet.containsAll(expectMap.keySet())); + Assert.assertTrue("The actual set is different from the expectation.", + expectMap.keySet().containsAll(fieldSet)); + } + + @Test + public void mapValueSetTest() { + final CacheKey key = new CacheKey("test_map_value_set"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertNull("The mapValueSet() method returned a non null value for a nonexistent map." + + "(If the map does not exist, null should be returned instead of an empty set)", + cacheStore.mapValueSet(key)); + + Assert.assertEquals("The number of test fields prepared is inconsistent.", + expectMap.size(), jedis.hset(keyString, expectMap).intValue()); + + Set valueSet = cacheStore.mapValueSet(key); + Assert.assertNotNull("Method returns 'null' for the existing map.", valueSet); + + Assert.assertTrue("The actual set is different from the expectation.", + valueSet.containsAll(expectMap.values())); + Assert.assertTrue("The actual set is different from the expectation.", + expectMap.values().containsAll(valueSet)); + } + + @Test + public void removeFieldTest() { + final CacheKey key = new CacheKey("test_remove_field"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field"; + final String value = "value"; + + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertFalse("Returns 'true' when trying to delete a field in a nonexistent map.", + cacheStore.removeField(key, "field")); + + Assert.assertEquals("Failed to add field for test.", + 1, jedis.hset(keyString, field, value).intValue()); + Assert.assertFalse("Returns 'true' when trying to delete a field that does not exist in the map.", + cacheStore.removeField(key, randomString(10))); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertEquals("Failed to add field for test.", + expectMap.size(), jedis.hset(keyString, expectMap).intValue()); + + for (String expectField : expectMap.keySet()) { + Assert.assertTrue("The attempt to delete an existing field failed: '" + expectField + "'.", + cacheStore.removeField(key, expectField)); + } + } + + @Test + public void containsFieldTest() { + final CacheKey key = new CacheKey("test_contains_field"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + final String field = "field"; + final String value = "value"; + + assertDeleteIfExist(jedis, keyString); + + Assert.assertFalse("Returns 'true' when trying to check for a map that does not exist.", + cacheStore.containsField(key, "field")); + + Assert.assertEquals("Failed to add field for test.", + 1, jedis.hset(keyString, field, value).intValue()); + Assert.assertFalse("Returns 'true' when trying to check for a field that does not exist.", + cacheStore.removeField(key, randomString(10))); + + Assert.assertTrue("An attempt to check for existing fields returned 'false'.", + cacheStore.containsField(key, field)); + } + + @Test + public void clearMapTest() { + final CacheKey key = new CacheKey("test_remove_field"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + + final Map expectMap = new HashMap<>(); + expectMap.put("field01", "value01"); + expectMap.put("field02", "value02"); + expectMap.put("field03", "value03"); + expectMap.put("field04", "value04"); + expectMap.put("field05", "value05"); + expectMap.put("field06", "value06"); + expectMap.put("field07", "value07"); + + assertDeleteIfExist(jedis, keyString); + + Assert.assertFalse("Attempt to empty nonexistent map succeeded.", cacheStore.clearMap(key)); + + Assert.assertEquals("Failed to add field for test.", + expectMap.size(), jedis.hset(keyString, expectMap).intValue()); + Assert.assertTrue("The operation to be tested failed.", cacheStore.clearMap(key)); + Assert.assertEquals("After the clear operation, the map still contains fields.", + 0, jedis.hlen(keyString).intValue()); + } } \ No newline at end of file diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStoreTest.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStoreTest.java index e6b68d3..3b7903b 100644 --- a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStoreTest.java +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisSingleCacheStoreTest.java @@ -17,18 +17,16 @@ package net.lamgc.cgj.bot.cache.redis; -import com.google.common.base.Throwables; import net.lamgc.cgj.bot.cache.CacheKey; import net.lamgc.cgj.bot.cache.SingleCacheStore; +import net.lamgc.cgj.bot.cache.convert.StringConverter; import net.lamgc.cgj.bot.cache.convert.StringToStringConverter; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.*; +import redis.clients.jedis.Jedis; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.lang.reflect.Field; + +import static net.lamgc.cgj.bot.cache.redis.util.RedisTestUtils.*; /** * @see RedisCacheStore @@ -36,139 +34,109 @@ import java.util.Map; */ public class RedisSingleCacheStoreTest { - private final static RedisCacheStoreFactory factory; - private final static TemporaryFolder tempFolder = TemporaryFolder.builder().build(); + private final static String KEY_PREFIX = "test:single"; + private final static StringConverter CONVERTER = new StringToStringConverter(); - static { - try { - tempFolder.create(); - } catch (IOException e) { - Assert.fail(Throwables.getStackTraceAsString(e)); - } - factory = new RedisCacheStoreFactory(); - try { - factory.initial(tempFolder.newFolder("cache-redis")); - } catch (IOException e) { - Assert.fail(Throwables.getStackTraceAsString(e)); - } + private static Jedis jedis; + private final RedisConnectionPool connectionPool = new RedisConnectionPool(); + private final SingleCacheStore cacheStore = + new RedisSingleCacheStore<>(connectionPool, KEY_PREFIX, CONVERTER); + + @BeforeClass + public static void beforeAllTest() { + jedis = new Jedis(); } - private final static SingleCacheStore cacheStore = factory.newSingleCacheStore("test:single", new StringToStringConverter()); + @AfterClass + public static void afterAllTest() { + if (jedis != null && jedis.isConnected()) { + jedis.close(); + } + } @Before public void before() { - Assert.assertTrue(cacheStore.clear()); + Assert.assertTrue("Clear execution failed before the test started.", cacheStore.clear()); + } + + @After + public void after() { + Assert.assertTrue("After the test, clear execution failed", cacheStore.clear()); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyPrefixTest() { + new RedisSingleCacheStore<>(connectionPool, "", CONVERTER); + } + + @Test(expected = NullPointerException.class) + public void nullPrefixTest() { + new RedisSingleCacheStore<>(connectionPool, null, CONVERTER); } @Test - public void nullThrowTest() { - final SingleCacheStore tempCacheStore = factory.newSingleCacheStore("test:single" + RedisUtils.KEY_SEPARATOR, new StringToStringConverter()); - final CacheKey key = new CacheKey("testKey"); - - // RedisSingleCacheStore - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.set(null, "testValue")); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.set(key, null)); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.get(null)); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setIfNotExist(null, "testValue")); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setIfNotExist(key, null)); - - // RedisCacheStore - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.exists(null)); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.getTimeToLive(null)); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setTimeToLive(null, 0)); - Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.remove(null)); + public void prefixCheck() throws NoSuchFieldException, IllegalAccessException { + final Field prefixField = RedisSingleCacheStore.class.getDeclaredField("keyPrefix"); + prefixField.setAccessible(true); + String prefix = (String) prefixField.get(new RedisSingleCacheStore<>(connectionPool, KEY_PREFIX, CONVERTER)); + Assert.assertTrue("The prefix does not contain a separator at the end.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + prefix = (String) prefixField.get(new RedisSingleCacheStore<>(connectionPool, + KEY_PREFIX + RedisUtils.KEY_SEPARATOR, CONVERTER)); + Assert.assertTrue("The separator at the end of the prefix is missing.", + prefix.endsWith(RedisUtils.KEY_SEPARATOR)); + prefixField.setAccessible(false); } - @Test - public void setAndGetTest() { - final CacheKey key = new CacheKey("testKey"); - final String value = "testValue"; - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); - Assert.assertEquals(value, cacheStore.get(key)); - Assert.assertTrue("Remove operation failed!", cacheStore.remove(key)); - Assert.assertNull("Set operation failed!", cacheStore.get(key)); + @Test + public void setTest() { + final CacheKey key = new CacheKey("test_set"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + String value = randomString(10); + assertDeleteIfExist(jedis, keyString); + + Assert.assertTrue("Failed to set value on specified key.", cacheStore.set(key, value)); + Assert.assertEquals("The value set does not match the expected value.", + value, jedis.get(keyString)); + + value = randomString(12); + Assert.assertTrue("Cannot rewritten the value of an existing key.", cacheStore.set(key, value)); + Assert.assertEquals("The rewritten value does not match the expected value.", + value, jedis.get(keyString)); } @Test public void setIfNotExistTest() { - final CacheKey key = new CacheKey("testKey"); - final String value = "testValue"; - final String value2 = "testValue02"; - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); + final CacheKey key = new CacheKey("test_set_if_not_exist"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + String value = randomString(10); + assertDeleteIfExist(jedis, keyString); - Assert.assertFalse(cacheStore.setIfNotExist(key, value2)); - Assert.assertEquals(value, cacheStore.get(key)); + Assert.assertTrue("Failed to set value on specified key.", cacheStore.setIfNotExist(key, value)); + Assert.assertEquals("The value set does not match the expected value.", + value, jedis.get(keyString)); + + value = randomString(12); + Assert.assertFalse("Write value to existing key succeeded.", cacheStore.setIfNotExist(key, value)); + Assert.assertNotEquals("The key value is modified and the method setIfNotExist() returns 'false'.", + value, jedis.get(keyString)); } @Test - public void expireTest() throws InterruptedException { - final CacheKey key = new CacheKey("testKey"); - final String value = "testValue"; + public void getTest() { + final CacheKey key = new CacheKey("test_get"); + final String keyString = RedisUtils.toRedisCacheKey(KEY_PREFIX, key); + String value = randomString(10); + assertDeleteIfExist(jedis, keyString); - // Cache - Assert.assertFalse(cacheStore.setTimeToLive(key, 300)); - Assert.assertEquals(-1, cacheStore.getTimeToLive(key)); + Assert.assertNull("The get() method returned a non null value for a nonexistent key.", + cacheStore.get(key)); - // TTL 到期被动检查测试: 使用 exists 经 expire 检查失败后返回 false. - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); - Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, 200)); - Assert.assertNotEquals(-1, cacheStore.getTimeToLive(key)); - Thread.sleep(300); - Assert.assertFalse(cacheStore.exists(key)); + Assert.assertTrue("Failed to set test key.", RedisUtils.isOk(jedis.set(keyString, value))); - // 取消 TTL 测试 - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); - Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, 200)); - Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, -1)); - Thread.sleep(300); - Assert.assertTrue(cacheStore.exists(key)); - Assert.assertEquals(-1, cacheStore.getTimeToLive(key)); + Assert.assertEquals("The value obtained does not match the expected value.", + value, cacheStore.get(key)); } - @Test - public void removeTest() { - final CacheKey key = new CacheKey("testKey"); - final String value = "testValue"; - - // 删除不存在Cache测试 - Assert.assertFalse(cacheStore.remove(key)); - // 删除存在的Cache测试 - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); - Assert.assertTrue(cacheStore.remove(key)); - } - - @Test - public void clearTest() { - final SingleCacheStore secondSingleCacheStore = - factory.newSingleCacheStore("test:single_b", new StringToStringConverter()); - final CacheKey key = new CacheKey("testKey"); - final String value = "testValue"; - - Assert.assertTrue("Set operation failed!", cacheStore.set(key, value)); - Assert.assertTrue("Set operation failed!", secondSingleCacheStore.set(key, value)); - - Assert.assertTrue(cacheStore.exists(key)); - Assert.assertTrue("Clear operation failed!", cacheStore.clear()); - Assert.assertFalse(cacheStore.exists(key)); - Assert.assertTrue(secondSingleCacheStore.exists(key)); - } - - @Test - public void sizeAndKeySetTest() { - Map expectedMap = new HashMap<>(); - expectedMap.put("test01", "testValue01"); - expectedMap.put("test02", "testValue02"); - expectedMap.put("test03", "testValue03"); - expectedMap.put("test04", "testValue04"); - expectedMap.put("test05", "testValue05"); - expectedMap.put("test06", "testValue06"); - - Assert.assertEquals(0, cacheStore.size()); - expectedMap.forEach((key, value) -> cacheStore.set(new CacheKey(key), value)); - Assert.assertEquals(expectedMap.size(), cacheStore.size()); - Assert.assertTrue(expectedMap.keySet().containsAll(cacheStore.keySet())); - Assert.assertTrue(cacheStore.keySet().containsAll(expectedMap.keySet())); - } - } diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisUtilsTest.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisUtilsTest.java new file mode 100644 index 0000000..ad634fa --- /dev/null +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/RedisUtilsTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 LamGC + * + * ContentGrabbingJi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License. + * + * ContentGrabbingJi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package net.lamgc.cgj.bot.cache.redis; + +import net.lamgc.cgj.bot.cache.CacheKey; +import org.junit.Assert; +import org.junit.Test; + +/** + * @see RedisUtils + */ +public class RedisUtilsTest { + + @Test + public void isOkTest() { + Assert.assertTrue(RedisUtils.isOk("OK")); + Assert.assertTrue(RedisUtils.isOk("ok")); + Assert.assertFalse(RedisUtils.isOk("Failed")); + } + + @Test + public void toRedisCacheKey() { + final CacheKey key = new CacheKey("test"); + final String prefix = "prefix"; + + Assert.assertEquals("prefix:test", RedisUtils.toRedisCacheKey(prefix, key)); + Assert.assertEquals("prefix:test", RedisUtils.toRedisCacheKey(prefix + RedisUtils.KEY_SEPARATOR, key)); + } + +} diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/SimpleRedisCacheStore.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/SimpleRedisCacheStore.java new file mode 100644 index 0000000..df2e875 --- /dev/null +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/SimpleRedisCacheStore.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 LamGC + * + * ContentGrabbingJi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License. + * + * ContentGrabbingJi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package net.lamgc.cgj.bot.cache.redis; + +public class SimpleRedisCacheStore extends RedisCacheStore { + + private final String keyPrefix; + + protected SimpleRedisCacheStore(RedisConnectionPool connectionPool, String keyPrefix) { + super(connectionPool); + this.keyPrefix = keyPrefix; + } + + @Override + protected String getKeyPrefix() { + return keyPrefix; + } + +} diff --git a/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/util/RedisTestUtils.java b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/util/RedisTestUtils.java new file mode 100644 index 0000000..33faa62 --- /dev/null +++ b/ContentGrabbingJi-CacheStore-redis/src/test/java/net/lamgc/cgj/bot/cache/redis/util/RedisTestUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 LamGC + * + * ContentGrabbingJi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License. + * + * ContentGrabbingJi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package net.lamgc.cgj.bot.cache.redis.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.lamgc.cgj.bot.cache.redis.RedisConnectionProperties; +import net.lamgc.cgj.bot.cache.redis.RedisUtils; +import org.junit.Assert; +import redis.clients.jedis.Jedis; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.Random; + +/** + * + * @author LamGC + */ +public final class RedisTestUtils { + + private RedisTestUtils() {} + + private final static Gson GSON_INSTANCE = new GsonBuilder() + .serializeNulls() + .setPrettyPrinting() + .create(); + + /** + * 创建测试用配置文件. + * @param testDirectory 组件文件夹. + * @param properties 配置对象. + * @throws IOException 当配置文件写入失败时抛出. + */ + public static void createConnectionProperties(File testDirectory, RedisConnectionProperties properties) + throws IOException { + File propertiesFile = new File(testDirectory, "Redis/" + RedisUtils.PROPERTIES_FILE_NAME); + Files.write(propertiesFile.toPath(), + GSON_INSTANCE.toJson(properties).getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + } + + /** + * 如果存在键, 则删除, 如果失败则判定断言失败. + * @param jedis Jedis 对象. + * @param keyString 待删除的键名. + */ + public static void assertDeleteIfExist(Jedis jedis, String keyString) { + if (jedis.exists(keyString)) { + Assert.assertEquals("The key used by the test is occupied and deletion failed.", + 1, jedis.del(keyString).intValue()); + } + } + + /** + * 随机字符串. + * @param length 长度. + * @return 返回指定长度的随机字符串. + */ + public static String randomString(int length) { + final char[] chars = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + }; + + StringBuilder buffer = new StringBuilder(length); + Random random = new Random(); + for (int i = 0; i < length; i++) { + buffer.append(chars[random.nextInt(chars.length)]); + } + return buffer.toString(); + } + +}