|
@@ -0,0 +1,186 @@
|
|
|
|
|
+package com.etotem.cfc.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
|
|
+import org.mockito.InjectMocks;
|
|
|
|
|
+import org.mockito.Mock;
|
|
|
|
|
+import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
|
+import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.anyString;
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.eq;
|
|
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 首页引导浮层「按账号记录 + 新账号必弹 + 内容变化重弹」回归测试。
|
|
|
|
|
+ */
|
|
|
|
|
+@ExtendWith(MockitoExtension.class)
|
|
|
|
|
+class GuideConfigServiceTest {
|
|
|
|
|
+
|
|
|
|
|
+ private static final ObjectMapper MAPPER = new ObjectMapper();
|
|
|
|
|
+
|
|
|
|
|
+ @Mock
|
|
|
|
|
+ private SysConfigService sysConfigService;
|
|
|
|
|
+
|
|
|
|
|
+ @Mock
|
|
|
|
|
+ private JdbcTemplate jdbcTemplate;
|
|
|
|
|
+
|
|
|
|
|
+ @InjectMocks
|
|
|
|
|
+ private GuideConfigService service;
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> step(String title) {
|
|
|
|
|
+ Map<String, Object> s = new LinkedHashMap<>();
|
|
|
|
|
+ s.put("icon", "🔍");
|
|
|
|
|
+ s.put("title", title);
|
|
|
|
|
+ s.put("desc", "描述-" + title);
|
|
|
|
|
+ s.put("isIntro", true);
|
|
|
|
|
+ return s;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> config(String version, String parentTitle, String childTitle) {
|
|
|
|
|
+ Map<String, Object> cfg = new LinkedHashMap<>();
|
|
|
|
|
+ cfg.put("enabled", true);
|
|
|
|
|
+ cfg.put("version", version);
|
|
|
|
|
+ cfg.put("positions", Arrays.asList("home", "child"));
|
|
|
|
|
+ cfg.put("parent", Collections.singletonList(step(parentTitle)));
|
|
|
|
|
+ cfg.put("child", Collections.singletonList(step(childTitle)));
|
|
|
|
|
+ return cfg;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void stubConfig(Map<String, Object> cfg) throws Exception {
|
|
|
|
|
+ when(sysConfigService.getValue("onboarding_guide_config")).thenReturn(MAPPER.writeValueAsString(cfg));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void stubSeen(String hash) {
|
|
|
|
|
+ if (hash == null) {
|
|
|
|
|
+ when(jdbcTemplate.queryForList(anyString(), any(Long.class), any(String.class)))
|
|
|
|
|
+ .thenReturn(Collections.emptyList());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Map<String, Object>> rows = new ArrayList<>();
|
|
|
|
|
+ rows.add(Collections.singletonMap("content_hash", hash));
|
|
|
|
|
+ when(jdbcTemplate.queryForList(anyString(), any(Long.class), any(String.class))).thenReturn(rows);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 新账号无记录_必弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+ stubSeen(null);
|
|
|
|
|
+
|
|
|
|
|
+ Result<Map<String, Object>> result = service.current(12L, "home", "parent");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.TRUE, result.getData().get("shouldShow"));
|
|
|
|
|
+ assertEquals("1.0", result.getData().get("version"));
|
|
|
|
|
+ assertEquals(cfg.get("parent"), result.getData().get("parent"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 已读过相同内容_不弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+ stubSeen(service.contentHash(cfg, "home", "parent"));
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.FALSE, service.current(12L, "home", "parent").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 文案变化_同版本号也重弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> before = config("1.0", "旧家长文案", "孩子版");
|
|
|
|
|
+ Map<String, Object> after = config("1.0", "新家长文案", "孩子版");
|
|
|
|
|
+ stubConfig(after);
|
|
|
|
|
+ stubSeen(service.contentHash(before, "home", "parent"));
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.TRUE, service.current(12L, "home", "parent").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 版本号变化_重弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> before = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ Map<String, Object> after = config("1.1", "家长版", "孩子版");
|
|
|
|
|
+ stubConfig(after);
|
|
|
|
|
+ stubSeen(service.contentHash(before, "home", "parent"));
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.TRUE, service.current(12L, "home", "parent").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 配置禁用_不弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ cfg.put("enabled", false);
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.FALSE, service.current(12L, "home", "parent").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 位置不匹配_不弹() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ cfg.put("positions", Collections.singletonList("home"));
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.FALSE, service.current(12L, "child", "child").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 同一账号家长与孩子位置互不影响() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+ stubSeen(service.contentHash(cfg, "home", "parent"));
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.FALSE, service.current(12L, "home", "parent").getData().get("shouldShow"));
|
|
|
|
|
+ assertEquals(Boolean.TRUE, service.current(12L, "child", "child").getData().get("shouldShow"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 家长内容变化不影响孩子指纹() throws Exception {
|
|
|
|
|
+ Map<String, Object> before = config("1.0", "旧家长文案", "孩子版");
|
|
|
|
|
+ Map<String, Object> after = config("1.0", "新家长文案", "孩子版");
|
|
|
|
|
+
|
|
|
|
|
+ assertNotEquals(service.contentHash(before, "home", "parent"), service.contentHash(after, "home", "parent"));
|
|
|
|
|
+ assertEquals(service.contentHash(before, "child", "child"), service.contentHash(after, "child", "child"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 同一位置按实际展示角色计算指纹() {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+
|
|
|
|
|
+ assertNotEquals(service.contentHash(cfg, "home", "parent"), service.contentHash(cfg, "home", "child"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void 无配置时按默认内容指纹且新账号可弹() {
|
|
|
|
|
+ stubSeen(null);
|
|
|
|
|
+
|
|
|
|
|
+ Result<Map<String, Object>> result = service.current(12L, "home", "parent");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(Boolean.TRUE, result.getData().get("shouldShow"));
|
|
|
|
|
+ assertEquals(64, String.valueOf(result.getData().get("contentHash")).length());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void ack_空指纹时按当前配置补齐() throws Exception {
|
|
|
|
|
+ Map<String, Object> cfg = config("1.0", "家长版", "孩子版");
|
|
|
|
|
+ stubConfig(cfg);
|
|
|
|
|
+
|
|
|
|
|
+ service.ack(12L, "home", "parent", "", "");
|
|
|
|
|
+
|
|
|
|
|
+ verify(jdbcTemplate).update(anyString(), eq(12L), eq("home"), eq("1.0"),
|
|
|
|
|
+ eq(service.contentHash(cfg, "home", "parent")));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|