package com.codechecker.redis; import com.codechecker.redis.detector.RedisWritePointDetector; import com.codechecker.redis.detector.WritePoint; import com.codechecker.redis.diff.ChangeType; import com.codechecker.redis.diff.SchemaChange; import com.codechecker.redis.diff.SchemaDiffer; import com.codechecker.redis.schema.JavaSchemaExtractor; import com.codechecker.redis.schema.SourceIndex; import com.codechecker.redis.schema.TypeSchema; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * 端到端组件级验证:租户缓存由 TenantVO 变为 CacheEnvelope{vo, expiresAtMs} 的结构变更。 */ class TenantScenarioTest { private static final Set PATTERNS = new HashSet<>(); static { PATTERNS.add("W01"); PATTERNS.add("W02"); PATTERNS.add("W03"); } @Test void detectsTenantEnvelopeWrapping() { String tenantVo = TestSupport.fixture("fixtures/tenant/TenantVO.txt"); String tenantLink = TestSupport.fixture("fixtures/tenant/TenantLinkModel.txt"); String helperOld = TestSupport.fixture("fixtures/tenant/HelperOld.txt"); String helperNew = TestSupport.fixture("fixtures/tenant/HelperNew.txt"); SourceIndex oldIndex = new SourceIndex(); oldIndex.addSource(tenantVo); oldIndex.addSource(tenantLink); oldIndex.addSource(helperOld); SourceIndex newIndex = new SourceIndex(); newIndex.addSource(tenantVo); newIndex.addSource(tenantLink); newIndex.addSource(helperNew); WritePoint oldWp = single(new RedisWritePointDetector(oldIndex, PATTERNS) .detect("Helper.java", helperOld)); WritePoint newWp = single(new RedisWritePointDetector(newIndex, PATTERNS) .detect("Helper.java", helperNew)); // key 推断 assertEquals("tenant:db:content:*", oldWp.getResolvedKeyPattern()); assertEquals("tenant:db:content:*", newWp.getResolvedKeyPattern()); // 写入点配对签名一致 assertEquals(oldWp.signature(), newWp.signature()); // value 类型推断 assertEquals("jnpf.model.TenantVO", oldWp.getResolvedValueType()); assertNotNull(newWp.getResolvedValueType()); assertTrue(newWp.getResolvedValueType().endsWith("CacheEnvelope")); JavaSchemaExtractor oldEx = new JavaSchemaExtractor(oldIndex, 8); JavaSchemaExtractor newEx = new JavaSchemaExtractor(newIndex, 8); TypeSchema oldSchema = oldEx.extract(oldWp.getResolvedValueType(), oldWp.isRootArray()); TypeSchema newSchema = newEx.extract(newWp.getResolvedValueType(), newWp.isRootArray()); // 旧结构包含顶层 dbName、linkList[].id assertTrue(oldSchema.getFields().containsKey("dbName")); assertTrue(oldSchema.getFields().containsKey("linkList[].id")); // 新结构包含 vo.dbName、expiresAtMs assertTrue(newSchema.getFields().containsKey("vo.dbName")); assertTrue(newSchema.getFields().containsKey("expiresAtMs")); List changes = new SchemaDiffer().diff(oldSchema, newSchema); List types = changes.stream().map(SchemaChange::getChangeType).collect(Collectors.toList()); assertTrue(types.contains(ChangeType.WRAPPER_ADDED), "应检测到包装层 vo"); assertTrue(types.contains(ChangeType.FIELD_PATH_MOVED), "应检测到字段路径迁移"); assertTrue(types.contains(ChangeType.FIELD_ADDED), "应检测到 expiresAtMs 新增"); } private WritePoint single(List wps) { assertEquals(1, wps.size(), "应恰好检测到 1 个写入点"); return wps.get(0); } }