157 lines
6.9 KiB
Java
157 lines
6.9 KiB
Java
package com.codechecker.cache.key;
|
||
|
||
import com.codechecker.cache.schema.SourceIndex;
|
||
import com.github.javaparser.StaticJavaParser;
|
||
import com.github.javaparser.ast.CompilationUnit;
|
||
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
|
||
import com.github.javaparser.ast.expr.Expression;
|
||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||
import org.junit.jupiter.api.Test;
|
||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||
|
||
/** Redis key 表达式静态模式推断单测(常量、format、拼接等)。 */
|
||
class RedisKeyResolverTest {
|
||
|
||
@Test
|
||
void resolvesStringFormatWithConstant() {
|
||
String source = ""
|
||
+ "package demo;\n"
|
||
+ "public class AttendanceService {\n"
|
||
+ " private static final String ATTENDANCE_BASE_SETTING_CACHE_KEY = "
|
||
+ "\"fbt:attendance:base_setting:cache:%s\";\n"
|
||
+ " public void save(String tenantId) {\n"
|
||
+ " String key = String.format(ATTENDANCE_BASE_SETTING_CACHE_KEY, tenantId);\n"
|
||
+ " }\n"
|
||
+ "}\n";
|
||
|
||
SourceIndex index = new SourceIndex();
|
||
index.addSource(source);
|
||
CompilationUnit cu = StaticJavaParser.parse(source);
|
||
ClassOrInterfaceDeclaration clazz = cu.getType(0).asClassOrInterfaceDeclaration();
|
||
MethodCallExpr formatCall = cu.findAll(MethodCallExpr.class).stream()
|
||
.filter(m -> "format".equals(m.getNameAsString()))
|
||
.findFirst()
|
||
.orElseThrow(IllegalStateException::new);
|
||
|
||
String pattern = new RedisKeyResolver(index).resolve(
|
||
formatCall, clazz, index.get("demo.AttendanceService"));
|
||
assertEquals("fbt:attendance:base_setting:cache:*", pattern);
|
||
}
|
||
|
||
@Test
|
||
void resolvesConstantConcatAndBuildMethod() {
|
||
String source = ""
|
||
+ "package jnpf.util;\n"
|
||
+ "public class TenantDbContentCacheHelper {\n"
|
||
+ " private static final String CACHE_KEY_PREFIX = \"tenant:db:content:\";\n"
|
||
+ " public String buildCacheKey(String encode) {\n"
|
||
+ " return CACHE_KEY_PREFIX + encode;\n"
|
||
+ " }\n"
|
||
+ " public void cache(String encode) {\n"
|
||
+ " String key = buildCacheKey(encode);\n"
|
||
+ " }\n"
|
||
+ "}\n";
|
||
|
||
SourceIndex index = new SourceIndex();
|
||
index.addSource(source);
|
||
CompilationUnit cu = StaticJavaParser.parse(source);
|
||
ClassOrInterfaceDeclaration clazz = cu.getType(0).asClassOrInterfaceDeclaration();
|
||
MethodCallExpr buildCall = cu.findAll(MethodCallExpr.class).stream()
|
||
.filter(m -> "buildCacheKey".equals(m.getNameAsString()))
|
||
.findFirst()
|
||
.orElseThrow(IllegalStateException::new);
|
||
|
||
String pattern = new RedisKeyResolver(index).resolve(
|
||
buildCall, clazz, index.get("jnpf.util.TenantDbContentCacheHelper"));
|
||
assertEquals("tenant:db:content:*", pattern);
|
||
}
|
||
|
||
@Test
|
||
void resolvesLocalVariableAssignedFromStringFormat() {
|
||
String source = ""
|
||
+ "package demo;\n"
|
||
+ "public class CombinationRepo {\n"
|
||
+ " private static final String COMBINATION_QUERY_KEY = "
|
||
+ "\"data_analysis:combination_query_key:%s:%s\";\n"
|
||
+ " public void write(String tenantId, String hashKey) {\n"
|
||
+ " String key = String.format(COMBINATION_QUERY_KEY, tenantId, hashKey);\n"
|
||
+ " redisSet(key);\n"
|
||
+ " }\n"
|
||
+ " void redisSet(String k) {}\n"
|
||
+ "}\n";
|
||
|
||
SourceIndex index = new SourceIndex();
|
||
index.addSource(source);
|
||
CompilationUnit cu = StaticJavaParser.parse(source);
|
||
ClassOrInterfaceDeclaration clazz = cu.getType(0).asClassOrInterfaceDeclaration();
|
||
// redisSet(key) 的实参 key —— 局部变量使用点
|
||
MethodCallExpr redisSet = cu.findAll(MethodCallExpr.class).stream()
|
||
.filter(m -> "redisSet".equals(m.getNameAsString()))
|
||
.findFirst()
|
||
.orElseThrow(IllegalStateException::new);
|
||
Expression keyArg = redisSet.getArgument(0);
|
||
|
||
String pattern = new RedisKeyResolver(index).resolve(
|
||
keyArg, clazz, index.get("demo.CombinationRepo"));
|
||
assertEquals("data_analysis:combination_query_key:*:*", pattern);
|
||
}
|
||
|
||
@Test
|
||
void localReassignmentUsesNearestBindingBeforeUse() {
|
||
String source = ""
|
||
+ "package demo;\n"
|
||
+ "public class ReassignRepo {\n"
|
||
+ " private static final String A = \"prefix:a:%s\";\n"
|
||
+ " private static final String B = \"prefix:b:%s\";\n"
|
||
+ " public void write(String id) {\n"
|
||
+ " String key = String.format(A, id);\n"
|
||
+ " key = String.format(B, id);\n"
|
||
+ " redisSet(key);\n"
|
||
+ " }\n"
|
||
+ " void redisSet(String k) {}\n"
|
||
+ "}\n";
|
||
|
||
SourceIndex index = new SourceIndex();
|
||
index.addSource(source);
|
||
CompilationUnit cu = StaticJavaParser.parse(source);
|
||
ClassOrInterfaceDeclaration clazz = cu.getType(0).asClassOrInterfaceDeclaration();
|
||
MethodCallExpr redisSet = cu.findAll(MethodCallExpr.class).stream()
|
||
.filter(m -> "redisSet".equals(m.getNameAsString()))
|
||
.findFirst()
|
||
.orElseThrow(IllegalStateException::new);
|
||
|
||
String pattern = new RedisKeyResolver(index).resolve(
|
||
redisSet.getArgument(0), clazz, index.get("demo.ReassignRepo"));
|
||
assertEquals("prefix:b:*", pattern);
|
||
}
|
||
|
||
@Test
|
||
void staticConstantStillPreferredOverLocalNameCollision() {
|
||
// 局部变量名与静态常量同名时:仍优先静态常量(保持旧语义)
|
||
String source = ""
|
||
+ "package demo;\n"
|
||
+ "public class Collision {\n"
|
||
+ " private static final String KEY = \"const:prefix:%s\";\n"
|
||
+ " public void write(String id) {\n"
|
||
+ " String KEY = \"local:\" + id;\n"
|
||
+ " redisSet(KEY);\n"
|
||
+ " }\n"
|
||
+ " void redisSet(String k) {}\n"
|
||
+ "}\n";
|
||
|
||
SourceIndex index = new SourceIndex();
|
||
index.addSource(source);
|
||
CompilationUnit cu = StaticJavaParser.parse(source);
|
||
ClassOrInterfaceDeclaration clazz = cu.getType(0).asClassOrInterfaceDeclaration();
|
||
MethodCallExpr redisSet = cu.findAll(MethodCallExpr.class).stream()
|
||
.filter(m -> "redisSet".equals(m.getNameAsString()))
|
||
.findFirst()
|
||
.orElseThrow(IllegalStateException::new);
|
||
|
||
String pattern = new RedisKeyResolver(index).resolve(
|
||
redisSet.getArgument(0), clazz, index.get("demo.Collision"));
|
||
assertEquals("const:prefix:%s", pattern);
|
||
}
|
||
}
|