feat(syslog): 为三个生产模块增加启动版本标识

- syslog-serve 增加 SYSLOG_BUILD_READY 启动日志
- syslog-consumer 增加 SYSLOG_BUILD_READY 启动日志
- 更新 syslog-consumer-rule 代码修订标识至 v63
- 输出模块名、代码修订号、运行环境及关键非敏感配置
- 支持通过容器日志快速确认实际部署的 JAR 版本
- 补充 syslog-serve 和 syslog-consumer 启动日志测试
- 不输出数据库密码、API 凭据等敏感信息
- 不修改 Maven POM 配置
This commit is contained in:
王绍宏
2026-09-17 18:47:25 +08:00
parent 8f1a8b6283
commit 2d297eb996
67 changed files with 3132 additions and 88 deletions
@@ -63,8 +63,30 @@ public class NormalizationCacheInvalidationService {
}
}
/**
* 清空规则专属 Spring Cache,并按设备精确广播 JetCache BOTH 失效。
*
* <p>JetCache 没有安全的全库 clear;调用方传入数据库中的全部设备 ID,既能删除共享 L2,
* 又能通过 syncLocal 让其他实例立即移除对应 L1,且不会误删同 Redis 中的其他业务数据。</p>
*/
public void invalidateAllRules(Collection<Long> deviceIds) {
clearSpring("normalizeRule");
metrics.invalidation("local");
if (deviceIds == null) return;
for (Long deviceId : deviceIds) {
if (deviceId == null) continue;
jetCacheInvalidator.rulesForDevice(deviceId);
metrics.invalidation("remote");
}
}
private void evictSpring(String cacheName, Object key) {
Cache cache = springCacheManager.getCache(cacheName);
if (cache != null) cache.evict(key);
}
private void clearSpring(String cacheName) {
Cache cache = springCacheManager.getCache(cacheName);
if (cache != null) cache.clear();
}
}
@@ -0,0 +1,85 @@
package com.Modules.NormalData.cache;
import com.Modules.NormalData.compiled.CompiledRuleCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringJoiner;
/** 通过 Redis Pub/Sub 让所有消费节点同步清理 JVM 本地编译规则缓存。 */
@Component
public class NormalizationCompiledCacheInvalidationBus implements MessageListener {
public static final String TOPIC = "xdr:norm:v1:compiled-rule:invalidate";
private static final String ALL = "ALL";
private static final String RULES_PREFIX = "RULES:";
private static final Logger log = LoggerFactory.getLogger(
NormalizationCompiledCacheInvalidationBus.class);
private final CompiledRuleCache compiledRuleCache;
private final StringRedisTemplate redisTemplate;
public NormalizationCompiledCacheInvalidationBus(CompiledRuleCache compiledRuleCache,
StringRedisTemplate redisTemplate) {
this.compiledRuleCache = compiledRuleCache;
this.redisTemplate = redisTemplate;
}
public void invalidateRulesEverywhere(Collection<Long> ruleIds) {
Set<Long> distinct = positiveIds(ruleIds);
if (distinct.isEmpty()) return;
for (Long ruleId : distinct) compiledRuleCache.invalidateRule(ruleId);
redisTemplate.convertAndSend(TOPIC, ruleMessage(distinct));
}
public void invalidateAllEverywhere() {
compiledRuleCache.invalidateAll();
redisTemplate.convertAndSend(TOPIC, ALL);
}
@Override
public void onMessage(Message message, byte[] pattern) {
if (message == null || message.getBody() == null) return;
String command = new String(message.getBody(), StandardCharsets.UTF_8).trim();
if (ALL.equals(command)) {
compiledRuleCache.invalidateAll();
return;
}
if (!command.startsWith(RULES_PREFIX)) {
log.warn("忽略未知的规则缓存失效广播: {}", command);
return;
}
String ids = command.substring(RULES_PREFIX.length());
if (ids.isEmpty()) return;
for (String value : ids.split(",")) {
try {
long ruleId = Long.parseLong(value);
if (ruleId > 0L) compiledRuleCache.invalidateRule(ruleId);
} catch (NumberFormatException invalidId) {
log.warn("忽略非法的规则缓存失效广播参数: {}", value);
}
}
}
private static Set<Long> positiveIds(Collection<Long> ruleIds) {
Set<Long> distinct = new LinkedHashSet<>();
if (ruleIds == null) return distinct;
for (Long ruleId : ruleIds) {
if (ruleId != null && ruleId > 0L) distinct.add(ruleId);
}
return distinct;
}
private static String ruleMessage(Collection<Long> ruleIds) {
StringJoiner values = new StringJoiner(",", RULES_PREFIX, "");
for (Long ruleId : ruleIds) values.add(ruleId.toString());
return values.toString();
}
}
@@ -0,0 +1,83 @@
package com.Modules.NormalData.cache;
import com.common.mapper.DeviceDeviceMapper;
import com.common.mapper.DmNormalizeRuleMapper;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/** 管理接口使用的规则缓存清理编排;不会清除设备、任务或其他业务 Redis 数据。 */
@Service
public class NormalizationRuleCacheManagementService {
private final NormalizationCacheInvalidationService invalidationService;
private final NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus;
private final DeviceDeviceMapper deviceMapper;
private final DmNormalizeRuleMapper ruleMapper;
public NormalizationRuleCacheManagementService(
NormalizationCacheInvalidationService invalidationService,
NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus,
DeviceDeviceMapper deviceMapper,
DmNormalizeRuleMapper ruleMapper) {
this.invalidationService = invalidationService;
this.compiledCacheInvalidationBus = compiledCacheInvalidationBus;
this.deviceMapper = deviceMapper;
this.ruleMapper = ruleMapper;
}
/** 清理一个设备的规则快照,并移除该设备当前关联规则的实体和编译缓存。 */
public ClearResult clearDevice(long deviceId) {
if (deviceId <= 0L) throw new IllegalArgumentException("deviceId 必须大于 0");
Set<Long> ruleIds = new LinkedHashSet<>();
addAllNonNull(ruleIds, nullSafe(ruleMapper.selectRuleIdsByDeviceId(deviceId)));
invalidationService.invalidateRulesForDevice(deviceId);
for (Long ruleId : ruleIds) {
invalidationService.invalidateRuleEntity(ruleId);
}
compiledCacheInvalidationBus.invalidateRulesEverywhere(ruleIds);
return new ClearResult("DEVICE", deviceId, 1, ruleIds.size());
}
/** 清理所有设备规则快照、规则实体和当前 JVM 的全部编译缓存。 */
public ClearResult clearAll() {
Set<Long> deviceIds = new LinkedHashSet<>();
addAllNonNull(deviceIds, deviceMapper.selectAllIds());
// 即使存在历史孤立关联,也应删除其规则快照键。
addAllNonNull(deviceIds, ruleMapper.selectAllAssociatedDeviceIds());
invalidationService.invalidateAllRules(deviceIds);
compiledCacheInvalidationBus.invalidateAllEverywhere();
return new ClearResult("ALL", null, deviceIds.size(), 0);
}
private static List<Long> nullSafe(List<Long> values) {
return values == null ? Collections.emptyList() : values;
}
private static void addAllNonNull(Set<Long> target, Collection<Long> values) {
if (values == null) return;
for (Long value : values) if (value != null) target.add(value);
}
public static final class ClearResult {
private final String scope;
private final Long deviceId;
private final int deviceCount;
private final int associatedRuleCount;
public ClearResult(String scope, Long deviceId, int deviceCount, int associatedRuleCount) {
this.scope = scope;
this.deviceId = deviceId;
this.deviceCount = deviceCount;
this.associatedRuleCount = associatedRuleCount;
}
public String getScope() { return scope; }
public Long getDeviceId() { return deviceId; }
public int getDeviceCount() { return deviceCount; }
public int getAssociatedRuleCount() { return associatedRuleCount; }
}
}
@@ -1,6 +1,5 @@
package com.Modules.NormalData.cache;
import com.Modules.NormalData.compiled.CompiledRuleCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@@ -14,21 +13,21 @@ public class NormalizeRuleCacheInvalidationListener {
private static final Logger log = LoggerFactory.getLogger(NormalizeRuleCacheInvalidationListener.class);
private final NormalizationCacheInvalidationService invalidationService;
private final CompiledRuleCache compiledRuleCache;
private final NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus;
private final NormalizationRuleMetrics metrics;
public NormalizeRuleCacheInvalidationListener(NormalizationCacheInvalidationService invalidationService,
CompiledRuleCache compiledRuleCache,
NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus,
NormalizationRuleMetrics metrics) {
this.invalidationService = invalidationService;
this.compiledRuleCache = compiledRuleCache;
this.compiledCacheInvalidationBus = compiledCacheInvalidationBus;
this.metrics = metrics;
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(NormalizeRuleChangedEvent event) {
metrics.invalidation("after_commit");
// 先移除跨 JVM 的规则事实,再清除本 JVM 所有历史 fingerprint 编译对象。
// 先移除跨 JVM 的规则事实,再广播清除所有 JVM 的历史 fingerprint 对象。
for (Long deviceId : event.getDeviceIds()) {
try {
invalidationService.invalidateRulesForDevice(deviceId);
@@ -39,7 +38,8 @@ public class NormalizeRuleCacheInvalidationListener {
}
}
try {
compiledRuleCache.invalidateRule(event.getRuleId());
compiledCacheInvalidationBus.invalidateRulesEverywhere(
java.util.Collections.singleton(event.getRuleId()));
} catch (RuntimeException cacheFailure) {
metrics.invalidation("failure");
// 数据库事实已提交;raw rules 的短 TTL 是最终一致性兜底。
@@ -192,6 +192,18 @@ public class CompiledRuleCache {
metrics.compiledCacheGauge(compiled.estimatedSize(), evictions.sum());
}
/** 清空当前 JVM 中所有规则编译结果、匹配画像及短失败缓存。 */
public void invalidateAll() {
compiled.invalidateAll();
matchProfiles.invalidateAll();
failures.invalidateAll();
matchFailures.invalidateAll();
loggedFailures.invalidateAll();
keysByRule.invalidateAll();
matchKeysByRule.invalidateAll();
metrics.compiledCacheGauge(0L, evictions.sum());
}
public long estimatedSize() { return compiled.estimatedSize(); }
public long compileSuccessCount() { return compileSuccess.sum(); }
public long compileFailureCount() { return compileFailure.sum(); }
@@ -21,6 +21,9 @@ public interface DeviceDeviceMapper {
*/
List<DeviceDevice> selectAll();
/** 规则缓存全量失效只需要主键,避免加载整行设备信息。 */
List<Long> selectAllIds();
/**
* 根据IP地址查询设备
*/
@@ -90,4 +93,4 @@ public interface DeviceDeviceMapper {
* 根据厂商查询设备
*/
List<DeviceDevice> selectByVendor(String vendor);
}
}
@@ -24,6 +24,10 @@ public interface DmNormalizeRuleMapper {
List<Long> selectDeviceIdsByRuleId(@Param("ruleId") Long ruleId);
List<Long> selectRuleIdsByDeviceId(@Param("deviceId") Long deviceId);
List<Long> selectAllAssociatedDeviceIds();
/**
* 根据ID更新数据标准化规则
* @param rule 数据标准化规则对象
@@ -0,0 +1,24 @@
package com.config;
import com.Modules.NormalData.cache.NormalizationCompiledCacheInvalidationBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/** 规则编译缓存的跨实例失效广播通道。 */
@Configuration
public class NormalizationCacheInvalidationRedisConfig {
@Bean
public RedisMessageListenerContainer normalizationCacheInvalidationListenerContainer(
RedisConnectionFactory connectionFactory,
NormalizationCompiledCacheInvalidationBus listener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listener,
new ChannelTopic(NormalizationCompiledCacheInvalidationBus.TOPIC));
return container;
}
}
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
public class SyslogBuildStartupLogger {
// 确认包含此功能修订,不代表唯一构建;后续功能修订时同步更新此标识。
static final String CODE_REVISION = "ASYNC_STATS_ADAPTIVE_RAW_V20260913_57";
static final String CODE_REVISION = "ASYNC_STATS_ADAPTIVE_RAW_V20260917_63";
private final Environment environment;
public SyslogBuildStartupLogger(Environment environment) {
@@ -0,0 +1,52 @@
package com.controllers;
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService;
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService.ClearResult;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.LinkedHashMap;
import java.util.Map;
/** 只清理标准化规则相关缓存,不执行现有 /api/cache/all 的全 Redis 删除。 */
@RestController
@RequestMapping("/api/syslog/normalization/cache")
public class NormalizationRuleCacheController {
private final NormalizationRuleCacheManagementService service;
public NormalizationRuleCacheController(NormalizationRuleCacheManagementService service) {
this.service = service;
}
@DeleteMapping("/devices/{deviceId}")
public ResponseEntity<Map<String, Object>> clearDevice(@PathVariable Long deviceId) {
if (deviceId == null || deviceId <= 0L) {
Map<String, Object> error = new LinkedHashMap<>();
error.put("cleared", false);
error.put("errorCode", "INVALID_DEVICE_ID");
error.put("message", "deviceId 必须大于 0");
return ResponseEntity.badRequest().body(error);
}
return ResponseEntity.ok(response(service.clearDevice(deviceId)));
}
@DeleteMapping
public Map<String, Object> clearAll() {
return response(service.clearAll());
}
private Map<String, Object> response(ClearResult result) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("cleared", true);
body.put("scope", result.getScope());
if (result.getDeviceId() != null) body.put("deviceId", result.getDeviceId());
body.put("deviceCount", result.getDeviceCount());
if (result.getDeviceId() != null) {
body.put("associatedRuleCount", result.getAssociatedRuleCount());
}
return body;
}
}
@@ -70,6 +70,10 @@
FROM device_device
</select>
<select id="selectAllIds" resultType="java.lang.Long">
SELECT id FROM device_device
</select>
<!-- 根据IP查询 -->
<select id="selectByIp" parameterType="java.lang.String" resultMap="BaseResultMap">
SELECT
@@ -96,6 +96,17 @@
WHERE normalize_rule_id = #{ruleId}
</select>
<select id="selectRuleIdsByDeviceId" parameterType="java.lang.Long" resultType="java.lang.Long">
SELECT normalize_rule_id
FROM dm_normalize_rule_device
WHERE device_id = #{deviceId}
</select>
<select id="selectAllAssociatedDeviceIds" resultType="java.lang.Long">
SELECT DISTINCT device_id
FROM dm_normalize_rule_device
</select>
<!-- 根据ID和租户ID查询 -->
<select id="selectByIdAndTenant" resultMap="BaseResultMap">
@@ -10,6 +10,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
class NormalizationCacheInvalidationServiceTest {
@Test
void evictsMatchingLegacyAndJetCacheKeys() {
@@ -35,4 +37,20 @@ class NormalizationCacheInvalidationServiceTest {
verify(jet).collectTask(8);
verify(jet).rulesForDevice(9L);
}
@Test
void clearsOnlyRuleSpringCacheAndBroadcastsEveryDevice() {
CacheManager manager = mock(CacheManager.class);
Cache rules = mock(Cache.class);
when(manager.getCache("normalizeRule")).thenReturn(rules);
NormalizationCacheInvalidator jet = mock(NormalizationCacheInvalidator.class);
NormalizationCacheInvalidationService service = new NormalizationCacheInvalidationService(
manager, jet, new NormalizationRuleMetrics());
service.invalidateAllRules(Arrays.asList(9L, null, 10L));
verify(rules).clear();
verify(jet).rulesForDevice(9L);
verify(jet).rulesForDevice(10L);
}
}
@@ -0,0 +1,65 @@
package com.Modules.NormalData.cache;
import com.Modules.NormalData.compiled.CompiledRuleCache;
import org.junit.jupiter.api.Test;
import org.springframework.data.redis.connection.DefaultMessage;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
class NormalizationCompiledCacheInvalidationBusTest {
@Test
void clearsLocalRulesAndPublishesOneClusterCommand() {
CompiledRuleCache cache = mock(CompiledRuleCache.class);
StringRedisTemplate redis = mock(StringRedisTemplate.class);
NormalizationCompiledCacheInvalidationBus bus =
new NormalizationCompiledCacheInvalidationBus(cache, redis);
bus.invalidateRulesEverywhere(Arrays.asList(7L, 8L, 8L, null));
verify(cache).invalidateRule(7L);
verify(cache).invalidateRule(8L);
verify(redis).convertAndSend(
NormalizationCompiledCacheInvalidationBus.TOPIC, "RULES:7,8");
}
@Test
void clearsAllLocallyAndPublishesClusterCommand() {
CompiledRuleCache cache = mock(CompiledRuleCache.class);
StringRedisTemplate redis = mock(StringRedisTemplate.class);
NormalizationCompiledCacheInvalidationBus bus =
new NormalizationCompiledCacheInvalidationBus(cache, redis);
bus.invalidateAllEverywhere();
verify(cache).invalidateAll();
verify(redis).convertAndSend(
NormalizationCompiledCacheInvalidationBus.TOPIC, "ALL");
}
@Test
void subscriberClearsCompiledCacheOnEveryInstance() {
CompiledRuleCache cache = mock(CompiledRuleCache.class);
NormalizationCompiledCacheInvalidationBus bus =
new NormalizationCompiledCacheInvalidationBus(
cache, mock(StringRedisTemplate.class));
bus.onMessage(message("RULES:7,8"), null);
bus.onMessage(message("ALL"), null);
verify(cache).invalidateRule(7L);
verify(cache).invalidateRule(8L);
verify(cache).invalidateAll();
}
private static DefaultMessage message(String body) {
return new DefaultMessage(
NormalizationCompiledCacheInvalidationBus.TOPIC.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
}
}
@@ -0,0 +1,79 @@
package com.Modules.NormalData.cache;
import com.common.mapper.DeviceDeviceMapper;
import com.common.mapper.DmNormalizeRuleMapper;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class NormalizationRuleCacheManagementServiceTest {
@Test
void clearsDeviceRuleViewsEntitiesAndCompiledRules() {
NormalizationCacheInvalidationService invalidation =
mock(NormalizationCacheInvalidationService.class);
NormalizationCompiledCacheInvalidationBus compiled =
mock(NormalizationCompiledCacheInvalidationBus.class);
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
when(rules.selectRuleIdsByDeviceId(40L)).thenReturn(Arrays.asList(7L, 8L, 8L));
NormalizationRuleCacheManagementService service =
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
NormalizationRuleCacheManagementService.ClearResult result = service.clearDevice(40L);
assertEquals("DEVICE", result.getScope());
assertEquals(40L, result.getDeviceId());
assertEquals(2, result.getAssociatedRuleCount());
verify(invalidation).invalidateRulesForDevice(40L);
verify(invalidation).invalidateRuleEntity(7L);
verify(invalidation).invalidateRuleEntity(8L);
verify(compiled).invalidateRulesEverywhere(
new java.util.LinkedHashSet<>(Arrays.asList(7L, 8L)));
}
@Test
void clearsAllDistinctDeviceViewsAndCompiledCache() {
NormalizationCacheInvalidationService invalidation =
mock(NormalizationCacheInvalidationService.class);
NormalizationCompiledCacheInvalidationBus compiled =
mock(NormalizationCompiledCacheInvalidationBus.class);
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
when(devices.selectAllIds()).thenReturn(Arrays.asList(40L, 41L));
when(rules.selectAllAssociatedDeviceIds()).thenReturn(Arrays.asList(41L, 42L));
NormalizationRuleCacheManagementService service =
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
NormalizationRuleCacheManagementService.ClearResult result = service.clearAll();
assertEquals("ALL", result.getScope());
assertEquals(3, result.getDeviceCount());
verify(invalidation).invalidateAllRules(
org.mockito.ArgumentMatchers.argThat(ids -> ids.equals(
new java.util.LinkedHashSet<>(Arrays.asList(40L, 41L, 42L)))));
verify(compiled).invalidateAllEverywhere();
}
@Test
void acceptsDeviceWithoutAssociatedRules() {
NormalizationCacheInvalidationService invalidation =
mock(NormalizationCacheInvalidationService.class);
NormalizationCompiledCacheInvalidationBus compiled =
mock(NormalizationCompiledCacheInvalidationBus.class);
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
when(rules.selectRuleIdsByDeviceId(40L)).thenReturn(Collections.emptyList());
NormalizationRuleCacheManagementService service =
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
assertEquals(0, service.clearDevice(40L).getAssociatedRuleCount());
verify(invalidation).invalidateRulesForDevice(40L);
}
}
@@ -101,6 +101,24 @@ class CompiledRuleCacheTest {
assertEquals(4, compiler.compiles.get());
}
@Test
void invalidatingAllRemovesEveryCompiledRule() {
CountingCompiler compiler = new CountingCompiler();
CompiledRuleCache cache = new CompiledRuleCache(compiler, properties());
RuleSource first = source("v1", "{\"mappers\":[]}");
RuleSource second = new RuleSource(9L, "rule-9", "rule-9", "json",
"{\"mappers\":[]}", true, "0");
cache.get(first, null);
cache.get(second, null);
assertEquals(2, compiler.compiles.get());
cache.invalidateAll();
assertEquals(0L, cache.estimatedSize());
cache.get(first, null);
cache.get(second, null);
assertEquals(4, compiler.compiles.get());
}
@Test
void diagnosticRuleTextKeepsExecutionFieldsAndRemovesEmbeddedSamples() {
String diagnostic = CompiledRuleCache.diagnosticRuleText("{"
@@ -0,0 +1,36 @@
package com.common.mapper;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class NormalizationRuleCacheMapperXmlTest {
@Test
void mapperXmlContainsCacheManagementQueries() throws Exception {
Configuration configuration = new Configuration();
parse(configuration, "mapper/DeviceDeviceMapper.xml");
parse(configuration, "mapper/DmNormalizeRuleMapper.xml");
assertTrue(configuration.hasStatement(
"com.common.mapper.DeviceDeviceMapper.selectAllIds"));
assertTrue(configuration.hasStatement(
"com.common.mapper.DmNormalizeRuleMapper.selectRuleIdsByDeviceId"));
assertTrue(configuration.hasStatement(
"com.common.mapper.DmNormalizeRuleMapper.selectAllAssociatedDeviceIds"));
}
private static void parse(Configuration configuration, String resource) throws Exception {
try (InputStream stream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(resource)) {
assertNotNull(stream, resource);
new XMLMapperBuilder(stream, configuration, resource,
configuration.getSqlFragments()).parse();
}
}
}
@@ -0,0 +1,58 @@
package com.controllers;
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class NormalizationRuleCacheControllerTest {
@Test
void exposesDeviceAndAllRuleCacheOperations() {
NormalizationRuleCacheManagementService service =
mock(NormalizationRuleCacheManagementService.class);
when(service.clearDevice(40L)).thenReturn(result("DEVICE", 40L, 1, 2));
when(service.clearAll()).thenReturn(result("ALL", null, 8, 0));
NormalizationRuleCacheController controller =
new NormalizationRuleCacheController(service);
ResponseEntity<Map<String, Object>> device = controller.clearDevice(40L);
Map<String, Object> all = controller.clearAll();
assertEquals(200, device.getStatusCodeValue());
assertEquals(true, device.getBody().get("cleared"));
assertEquals("DEVICE", device.getBody().get("scope"));
assertEquals(40L, device.getBody().get("deviceId"));
assertEquals("ALL", all.get("scope"));
assertEquals(8, all.get("deviceCount"));
assertFalse(all.containsKey("associatedRuleCount"));
verify(service).clearDevice(40L);
verify(service).clearAll();
}
@Test
void rejectsNonPositiveDeviceIdWithoutCallingService() {
NormalizationRuleCacheManagementService service =
mock(NormalizationRuleCacheManagementService.class);
NormalizationRuleCacheController controller =
new NormalizationRuleCacheController(service);
ResponseEntity<Map<String, Object>> response = controller.clearDevice(0L);
assertEquals(400, response.getStatusCodeValue());
assertEquals("INVALID_DEVICE_ID", response.getBody().get("errorCode"));
}
private static NormalizationRuleCacheManagementService.ClearResult result(
String scope, Long deviceId, int deviceCount, int ruleCount) {
return new NormalizationRuleCacheManagementService.ClearResult(
scope, deviceId, deviceCount, ruleCount);
}
}