R1 先行批:question-center 后端(SB 2.7.18,23 单测全绿)+ 建表脚本 + 仓库说明
build / build-push (push) Successful in 51s

This commit is contained in:
chenxing
2026-10-22 10:13:00 +08:00
parent 380df08271
commit 907063a0b6
30 changed files with 2693 additions and 2 deletions
@@ -0,0 +1,138 @@
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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> row(String uuid) {
Map<String, Object> r = new HashMap<>();
r.put("answer_uuid", uuid);
r.put("is_deleted", 0);
return r;
}
static Map<String, Object> group(String key, Map<String, Object>... fields) {
Map<String, Object> g = new HashMap<>();
g.put("key", key);
g.put("fields", Arrays.asList(fields));
return g;
}
static Map<String, Object> field(String key, String label, String type, Map<String, Object>... options) {
Map<String, Object> 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<String, Object> option(String label, String value) {
Map<String, Object> o = new HashMap<>();
o.put("label", label);
o.put("value", value);
return o;
}
}