package cn.iocoder.yudao.module.mes.framework.config; import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; /** * 字典解析器测试:守住「字典优先、配置文件兜底」的合并规则与「绝不抛异常」的约定。 */ class MesTraceConfigResolverTest { private static final String YAML_URL = "http://yaml.example.org/trace.html"; private static final String DICT_URL = "http://1.95.78.124:9008/prod/trace.html"; private final DictDataApi dictDataApi = mock(DictDataApi.class); @Test void dictValueWinsOverYaml() { stubDict(dict("追溯网址", DICT_URL)); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(DICT_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.DICT, snapshot.traceUrlSource()); } @Test void fallsBackToYamlWhenDictTypeMissing() { stubDict(); // 字典类型尚未初始化,属于正常的「尚未配置」路径 MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(YAML_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.YAML, snapshot.traceUrlSource()); } @Test void disabledDictItemIsTreatedAsAbsent() { // 禁用是不删数据即停用某项配置的手段,必须按「未配置」处理 DictDataRespDTO disabled = dict("追溯网址", DICT_URL); disabled.setStatus(CommonStatusEnum.DISABLE.getStatus()); stubDict(disabled); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(YAML_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.YAML, snapshot.traceUrlSource()); } @Test void blankDictValueFallsBackToYaml() { stubDict(dict("追溯网址", " ")); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(YAML_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.YAML, snapshot.traceUrlSource()); } @Test void invalidDictValueFallsBackToYamlAndFlagsSource() { // 字典里填了内网地址:降级回配置文件而不是抛异常,避免一次手误让全平台导出停摆 stubDict(dict("追溯网址", "http://192.168.1.1/trace.html")); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(YAML_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.YAML_INVALID_DICT, snapshot.traceUrlSource()); } @Test void dictReadFailureFallsBackToYamlWithoutThrowing() { when(dictDataApi.getDictDataList(MesTraceConfigResolver.DICT_TYPE)) .thenThrow(new RuntimeException("数据库抖动")); MesTraceConfigSnapshot snapshot = assertDoesNotThrow(() -> newResolver().resolveSnapshot()); assertEquals(YAML_URL, snapshot.traceUrl()); assertEquals(MesTraceUrlSource.YAML, snapshot.traceUrlSource()); } @Test void dictIsReadOncePerCacheWindow() { stubDict(dict("追溯网址", DICT_URL)); MesTraceConfigResolver resolver = newResolver(); resolver.resolveSnapshot(); resolver.resolveSnapshot(); resolver.resolveSnapshot(); // 标签导出会成千上万次取快照,绝不能每次都查库 verify(dictDataApi, times(1)).getDictDataList(MesTraceConfigResolver.DICT_TYPE); } @Test void resolvesOtherFieldsIndependentlyFromDict() { stubDict(dict("追溯网址", DICT_URL), dict("生产经营者名称", "新疆塔里木河种业股份有限公司"), dict("进口商名称", "某进口商"), dict("是否进口种子", "true")); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals("新疆塔里木河种业股份有限公司", snapshot.operatorName()); assertEquals("某进口商", snapshot.importerName()); assertTrue(snapshot.imported()); } @Test void fieldsFallBackIndependentlyWhenOnlyUrlIsConfigured() { // 只配了追溯网址:其余三项仍应回退配置文件,而不是被清空 stubDict(dict("追溯网址", DICT_URL)); MesTraceConfigSnapshot snapshot = newResolver().resolveSnapshot(); assertEquals(DICT_URL, snapshot.traceUrl()); assertEquals("YAML 生产经营者", snapshot.operatorName()); assertEquals("YAML 进口商", snapshot.importerName()); assertFalse(snapshot.imported()); } @Test void unrecognizedImportedValueFallsBackToYaml() { stubDict(dict("是否进口种子", "也许吧")); assertFalse(newResolver().resolveSnapshot().imported()); } @Test void firstEnabledItemWinsWhenDuplicated() { // 同一标签重复配置时以排序靠前者为准(底层查询已按 sort 排序) stubDict(dict("追溯网址", DICT_URL), dict("追溯网址", "http://second.example.org/trace.html")); assertEquals(DICT_URL, newResolver().resolveSnapshot().traceUrl()); } private MesTraceConfigResolver newResolver() { MesTraceConfigResolver resolver = new MesTraceConfigResolver(); MesProperties properties = new MesProperties(); properties.setTraceBaseUrl(YAML_URL); properties.setOperatorName("YAML 生产经营者"); properties.setImporterName("YAML 进口商"); properties.setImported(false); ReflectionTestUtils.setField(resolver, "dictDataApi", dictDataApi); ReflectionTestUtils.setField(resolver, "mesProperties", properties); return resolver; } private void stubDict(DictDataRespDTO... data) { when(dictDataApi.getDictDataList(MesTraceConfigResolver.DICT_TYPE)).thenReturn(List.of(data)); } private static DictDataRespDTO dict(String label, String value) { DictDataRespDTO dto = new DictDataRespDTO(); dto.setDictType(MesTraceConfigResolver.DICT_TYPE); dto.setLabel(label); dto.setValue(value); dto.setStatus(CommonStatusEnum.ENABLE.getStatus()); return dto; } }