package com.yunhai.question.fc; import com.fasterxml.jackson.databind.ObjectMapper; import com.yunhai.platform.openapi.core.YschOpenCall; import com.yunhai.platform.utils.exception.DException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * FormCenterClient 单元测试(mock YschOpenCall): * ① 业务拒绝(DException=FC 信封 code!=0)不重试直接转 Reject; * ② 网络/信封异常重试 2 次后转 Unavailable;③ answers 分页解析(rows/nextCursor/after 透传)。 */ class FormCenterClientTest { private YschOpenCall openCall; private FormCenterClient client; @BeforeEach void setUp() { openCall = mock(YschOpenCall.class); client = new FormCenterClient(openCall, new ObjectMapper()); } @Test void fetchSchemaReturnsData() { Map data = new HashMap<>(); data.put("formUuid", "U1"); data.put("version", 2); data.put("groups", Arrays.asList( group("base", field("q1", "你的性别", "radio", option("男", "m"), option("女", "f"))))); when(openCall.get(anyString(), anyString(), anyMap(), eq(Map.class))).thenReturn(data); Map got = client.fetchSchema(27L, "U1"); assertEquals("U1", got.get("formUuid")); assertEquals(1, client.parseFields(got).size()); assertEquals("radio", client.parseFields(got).get(0).get("type")); } @Test void fcBusinessRejectIsNotRetried() { when(openCall.get(anyString(), anyString(), anyMap(), eq(Map.class))) .thenThrow(new DException("表单尚未发布:q-001")); FormCenterClient.FormCenterRejectException e = assertThrows( FormCenterClient.FormCenterRejectException.class, () -> client.fetchSchema(27L, "U1")); assertTrue(e.getMessage().contains("表单尚未发布")); verify(openCall, times(1)).get(anyString(), anyString(), anyMap(), eq(Map.class)); } @Test void networkFailureRetriesTwiceThenUnavailable() { when(openCall.get(anyString(), anyString(), anyMap(), eq(Map.class))) .thenThrow(new RuntimeException("connect refused")); assertThrows(FormCenterClient.FormCenterUnavailableException.class, () -> client.fetchSchema(27L, "U1")); verify(openCall, times(FormCenterClient.MAX_ATTEMPTS)) .get(anyString(), anyString(), anyMap(), eq(Map.class)); } @Test @SuppressWarnings("unchecked") void pullAnswersParsesRowsAndCursor() { Map page = new HashMap<>(); page.put("rows", Arrays.asList(row("a1"), row("a2"))); page.put("nextCursor", "a2"); when(openCall.get(anyString(), anyString(), anyMap(), eq(Map.class))).thenReturn(page); FormCenterClient.AnswersPage got = client.pullAnswers(27L, "U1", null, 500); assertEquals(2, got.getRows().size()); assertEquals("a2", got.getNextCursor()); assertEquals("a1", got.getRows().get(0).get("answer_uuid")); } @Test void pullAnswersEndOfWindowHasNullCursor() { Map page = new HashMap<>(); page.put("rows", Arrays.asList()); page.put("nextCursor", null); when(openCall.get(anyString(), anyString(), anyMap(), eq(Map.class))).thenReturn(page); FormCenterClient.AnswersPage got = client.pullAnswers(27L, "U1", "a2", 500); assertEquals(null, got.getNextCursor()); assertTrue(got.getRows().isEmpty()); } private static Map row(String uuid) { Map r = new HashMap<>(); r.put("answer_uuid", uuid); r.put("is_deleted", 0); return r; } static Map group(String key, Map... fields) { Map g = new HashMap<>(); g.put("key", key); g.put("fields", Arrays.asList(fields)); return g; } static Map field(String key, String label, String type, Map... options) { Map f = new HashMap<>(); f.put("key", key); f.put("label", label); f.put("type", type); f.put("options", Arrays.asList(options)); return f; } static Map option(String label, String value) { Map o = new HashMap<>(); o.put("label", label); o.put("value", value); return o; } }