|
@@ -0,0 +1,311 @@
|
|
|
|
|
+package com.etotem.cfc.unit;
|
|
|
|
|
+
|
|
|
|
|
+import com.etotem.cfc.constant.AttributionLowSideRules;
|
|
|
|
|
+import com.etotem.cfc.service.attribution.AttributionComplianceGate;
|
|
|
|
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.fail;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 契约级单测:谷禾 v2 低侧口径·证据码隔离(能力键 {@code guhe.gate.evidenceIsolate})。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>契约来源(2026-11-08《浠艾福平台程序修改计划(guhe_v2 兼容 · 知识库低侧口径 · 专家同步)》§三任务分解 T11,P1):
|
|
|
|
|
+ * <blockquote>
|
|
|
|
|
+ * T11 证据码隔离(G-3):<b>低侧机制证据标 PUB/KB;COM 只作存在性记录;未核实编号标「未核实」</b>。
|
|
|
|
|
+ * <br>§一 合规门禁(横切)③:证据码隔离(<b>COM 不进推荐依据</b>)。
|
|
|
|
|
+ * </blockquote></p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>契约语义</b>:本能力是<b>合规门禁 G-3 证据码隔离</b>——归因结论 / 推荐的每条证据都必须被举证「证据码」,
|
|
|
|
|
+ * 证据码隔离规则限定证据进推荐依据的口径:
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>{@code PUB}(公开文献)/{@code KB}(知识库)→ 可进推荐依据(低侧机制证据标 PUB/KB);</li>
|
|
|
|
|
+ * <li>{@code COM}(存在性记录)→ <b>只作存在性记录,绝不能进推荐依据</b>;</li>
|
|
|
|
|
+ * <li>未核实的编号 → 统一标「未核实」,不得充当推荐依据。</li>
|
|
|
|
|
+ * </ul></p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>实现现状(src/main 探测,2026-11-08)</b>:横切合规门禁框架已落地——
|
|
|
|
|
+ * G-1 低侧红线组({@link AttributionLowSideRules} / {@link AttributionComplianceGate})已合入主链
|
|
|
|
|
+ * ({@code attribution_only} / {@code no_causal_bridge} / {@code no_prescription}),
|
|
|
|
|
+ * 但 <b>G-3 证据码隔离未落地</b>:{@link AttributionLowSideRules} 无 {@code RULE_EVIDENCE_ISOLATE} /
|
|
|
|
|
+ * {@code EVIDENCE_PUB} / {@code EVIDENCE_KB} / {@code EVIDENCE_COM} / {@code EVIDENCE_UNVERIFIED} 常量,
|
|
|
|
|
+ * {@link AttributionComplianceGate} 无 {@code tagEvidence} / {@code canEnterRecommendationBasis} /
|
|
|
|
|
+ * {@code isExistenceRecordOnly} / {@code isolateForRecommendation} 方法(全库 0 引用)。
|
|
|
|
|
+ * 因此本测试以「反射常量探测 + 反射方法契约探测」双通道验证契约缺失:能编译、能运行、断言失败(红),
|
|
|
|
|
+ * 且失败断言原文清晰可贴。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>RED 预期</b>:常量用例与门禁方法用例均应失败(红)——G-3 证据码隔离未落地。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>GREEN 升级指引</b>:实现者在 {@link AttributionLowSideRules} 增加
|
|
|
|
|
+ * {@code RULE_EVIDENCE_ISOLATE="evidence_isolate"}、{@code EVIDENCE_PUB="PUB"}、
|
|
|
|
|
+ * {@code EVIDENCE_KB="KB"}、{@code EVIDENCE_COM="COM"}、{@code EVIDENCE_UNVERIFIED="未核实"} 常量,
|
|
|
|
|
+ * 并在 {@link AttributionComplianceGate} 暴露以下确定性方法后,本测试自动转绿:
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>{@code String tagEvidence(String evidenceCode)} —— 证据码规范化:PUB/KB/COM 原样保留;空/null/未知编号 → "未核实"</li>
|
|
|
|
|
+ * <li>{@code boolean canEnterRecommendationBasis(String evidenceCode)} —— 仅 PUB/KB 可进推荐依据;COM/未核实/未知 → false</li>
|
|
|
|
|
+ * <li>{@code boolean isExistenceRecordOnly(String evidenceCode)} —— 仅 COM 为存在性记录</li>
|
|
|
|
|
+ * <li>{@code List<String> isolateForRecommendation(List<String> evidenceCodes)} —— 过滤仅保留 PUB/KB(保序,剔除 COM 与「未核实」)</li>
|
|
|
|
|
+ * </ul></p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>权限流说明</b>:本能力为后端内部横切合规门禁(L4 出结论前 / L5 出文档前),无用户权限维度,
|
|
|
|
|
+ * 权限流不适用,以说明性用例标注。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+class GuheEvidenceIsolateContractTest {
|
|
|
|
|
+
|
|
|
|
|
+ /** G-3 期望的规则码(区别于 G-1 的 association_only / no_causal_bridge / no_prescription) */
|
|
|
|
|
+ private static final String RULE_EVIDENCE_ISOLATE = "evidence_isolate";
|
|
|
|
|
+
|
|
|
|
|
+ /** 证据码:公开文献(可进推荐依据) */
|
|
|
|
|
+ private static final String EVIDENCE_PUB = "PUB";
|
|
|
|
|
+
|
|
|
|
|
+ /** 证据码:知识库(可进推荐依据) */
|
|
|
|
|
+ private static final String EVIDENCE_KB = "KB";
|
|
|
|
|
+
|
|
|
|
|
+ /** 证据码:存在性记录(只作存在性记录,不进推荐依据) */
|
|
|
|
|
+ private static final String EVIDENCE_COM = "COM";
|
|
|
|
|
+
|
|
|
|
|
+ /** 未核实标注(未核实编号统一标此值) */
|
|
|
|
|
+ private static final String EVIDENCE_UNVERIFIED = "未核实";
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 正常流:规则层应暴露 G-3 证据码隔离常量
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-1:AttributionLowSideRules 应暴露 RULE_EVIDENCE_ISOLATE 规则码")
|
|
|
|
|
+ void lowSideRules_exposesEvidenceIsolateRuleCode() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, "RULE_EVIDENCE_ISOLATE");
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.gate.evidenceIsolate] AttributionLowSideRules 缺少 RULE_EVIDENCE_ISOLATE 规则码:"
|
|
|
|
|
+ + "T11(G-3)要求独立「证据码隔离」合规规则——低侧机制证据标 PUB/KB;COM 只作存在性记录;"
|
|
|
|
|
+ + "未核实编号标「未核实」。当前仅落地 G-1 三规则(association_only / no_causal_bridge / no_prescription),"
|
|
|
|
|
+ + "未区分「G-3 证据码隔离」。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.gate.evidenceIsolate] RULE_EVIDENCE_ISOLATE 常量值不应为 null。");
|
|
|
|
|
+ assertTrue(RULE_EVIDENCE_ISOLATE.equals(String.valueOf(v)),
|
|
|
|
|
+ "[guhe.gate.evidenceIsolate] RULE_EVIDENCE_ISOLATE 应取值为 \"" + RULE_EVIDENCE_ISOLATE
|
|
|
|
|
+ + "\",实际为 \"" + v + "\"。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.gate.evidenceIsolate] 读取 RULE_EVIDENCE_ISOLATE 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-2:AttributionLowSideRules 应暴露可进推荐依据的证据码 PUB/KB")
|
|
|
|
|
+ void lowSideRules_exposesRecommendationBasisEvidenceCodes() {
|
|
|
|
|
+ assertConstantEquals("EVIDENCE_PUB", EVIDENCE_PUB);
|
|
|
|
|
+ assertConstantEquals("EVIDENCE_KB", EVIDENCE_KB);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-3:AttributionLowSideRules 应暴露存在性记录证据码 COM 与「未核实」标注")
|
|
|
|
|
+ void lowSideRules_exposesComAndUnverifiedMarkers() {
|
|
|
|
|
+ assertConstantEquals("EVIDENCE_COM", EVIDENCE_COM);
|
|
|
|
|
+ assertConstantEquals("EVIDENCE_UNVERIFIED", EVIDENCE_UNVERIFIED);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 正常流:门禁应暴露证据码隔离方法(tagEvidence / canEnterRecommendationBasis)
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-4:AttributionComplianceGate 应暴露 tagEvidence(String) 证据码规范化")
|
|
|
|
|
+ void gate_exposesTagEvidence() {
|
|
|
|
|
+ assertMethodExists("tagEvidence", 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-5:PUB/KB 低侧机制证据经 tagEvidence 应原样保留")
|
|
|
|
|
+ void tagEvidence_pubKb_unchanged() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertEquals(EVIDENCE_PUB, call(gate, "tagEvidence", EVIDENCE_PUB),
|
|
|
|
|
+ "PUB 证据码应原样保留(低侧机制证据标 PUB/KB)");
|
|
|
|
|
+ assertEquals(EVIDENCE_KB, call(gate, "tagEvidence", EVIDENCE_KB),
|
|
|
|
|
+ "KB 证据码应原样保留(低侧机制证据标 PUB/KB)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-6:PUB/KB 证据码应可进推荐依据")
|
|
|
|
|
+ void canEnterRecommendationBasis_pubKb_true() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertTrue(callBoolean(gate, "canEnterRecommendationBasis", EVIDENCE_PUB),
|
|
|
|
|
+ "PUB 证据码可进推荐依据");
|
|
|
|
|
+ assertTrue(callBoolean(gate, "canEnterRecommendationBasis", EVIDENCE_KB),
|
|
|
|
|
+ "KB 证据码可进推荐依据");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 边界流:未核实编号标「未核实」且不得进推荐依据
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-1:未知/空/未核实证据编号应统一标「未核实」")
|
|
|
|
|
+ void tagEvidence_unknownOrBlank_markedUnverified() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertEquals(EVIDENCE_UNVERIFIED, call(gate, "tagEvidence", "COM-870"),
|
|
|
|
|
+ "未核实的编号(如 COM-870)应统一标「未核实」");
|
|
|
|
|
+ assertEquals(EVIDENCE_UNVERIFIED, call(gate, "tagEvidence", ""),
|
|
|
|
|
+ "空证据码应标「未核实」");
|
|
|
|
|
+ assertEquals(EVIDENCE_UNVERIFIED, call(gate, "tagEvidence", null),
|
|
|
|
|
+ "null 证据码应标「未核实」");
|
|
|
|
|
+ assertEquals(EVIDENCE_UNVERIFIED, call(gate, "tagEvidence", "未知-编号"),
|
|
|
|
|
+ "未知证据码应标「未核实」");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-2:未核实编号不得进推荐依据")
|
|
|
|
|
+ void canEnterRecommendationBasis_unverified_false() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertFalse(callBoolean(gate, "canEnterRecommendationBasis", EVIDENCE_UNVERIFIED),
|
|
|
|
|
+ "「未核实」必须排除出推荐依据(未核实编号标「未核实」后不得充当依据)");
|
|
|
|
|
+ assertFalse(callBoolean(gate, "canEnterRecommendationBasis", "COM-870"),
|
|
|
|
|
+ "未核实的编号(如 COM-870)不得进推荐依据");
|
|
|
|
|
+ assertFalse(callBoolean(gate, "canEnterRecommendationBasis", ""),
|
|
|
|
|
+ "空证据码不得进推荐依据");
|
|
|
|
|
+ assertFalse(callBoolean(gate, "canEnterRecommendationBasis", null),
|
|
|
|
|
+ "null 证据码不得进推荐依据");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 合规流:COM 只作存在性记录、不进推荐依据(G-3 核心红线)
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("合规流-1:COM 证据码不得进推荐依据")
|
|
|
|
|
+ void canEnterRecommendationBasis_com_false() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertFalse(callBoolean(gate, "canEnterRecommendationBasis", EVIDENCE_COM),
|
|
|
|
|
+ "COM 只作存在性记录,必须排除出推荐依据(§一 合规门禁③:COM 不进推荐依据)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("合规流-2:COM 证据码应判定为存在性记录,PUB 不是")
|
|
|
|
|
+ void isExistenceRecordOnly_com_true_pubFalse() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ assertTrue(callBoolean(gate, "isExistenceRecordOnly", EVIDENCE_COM),
|
|
|
|
|
+ "COM 应判定为存在性记录(只作存在性记录,不进推荐依据)");
|
|
|
|
|
+ assertFalse(callBoolean(gate, "isExistenceRecordOnly", EVIDENCE_PUB),
|
|
|
|
|
+ "PUB 不是存在性记录(可进推荐依据)");
|
|
|
|
|
+ assertFalse(callBoolean(gate, "isExistenceRecordOnly", EVIDENCE_KB),
|
|
|
|
|
+ "KB 不是存在性记录(可进推荐依据)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("合规流-3:isolateForRecommendation 应过滤仅保留 PUB/KB(剔除 COM 与「未核实」)")
|
|
|
|
|
+ void isolateForRecommendation_filtersComAndUnverified() {
|
|
|
|
|
+ Object gate = new AttributionComplianceGate();
|
|
|
|
|
+ List<String> filtered = callListString(gate, "isolateForRecommendation",
|
|
|
|
|
+ Arrays.asList(EVIDENCE_PUB, EVIDENCE_COM, EVIDENCE_KB, EVIDENCE_UNVERIFIED, "COM-870"));
|
|
|
|
|
+ assertNotNull(filtered, "isolateForRecommendation 不应返回 null");
|
|
|
|
|
+ assertEquals(Arrays.asList(EVIDENCE_PUB, EVIDENCE_KB), filtered,
|
|
|
|
|
+ "推荐依据证据码应仅含 PUB/KB(保序);COM 与「未核实」必须被隔离,"
|
|
|
|
|
+ + "实际为 " + filtered);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 权限流(不适用):后端内部横切合规门禁,无用户权限维度
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("权限流:本能力为后端内部横切合规门禁,无用户权限维度,权限流不适用")
|
|
|
|
|
+ void permissionFlow_notApplicable() {
|
|
|
|
|
+ // 说明性用例:契约 AC 为「归因证据码 → 合规门禁隔离」内部规则,无权限校验维度。
|
|
|
|
|
+ // 不产生断言失败;仅为三流覆盖标注「不适用」。
|
|
|
|
|
+ assertTrue(true, "权限流不适用:guhe.gate.evidenceIsolate 为后端内部横切合规门禁,无用户权限维度。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 反射辅助
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ /** 按字段名探测并读取 public static final 常量,缺失即以清晰原文失败(红)。 */
|
|
|
|
|
+ private static void assertConstantEquals(String fieldName, String expected) {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, fieldName);
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.gate.evidenceIsolate] AttributionLowSideRules 缺少 " + fieldName
|
|
|
|
|
+ + " 常量:T11(G-3)要求证据码隔离常量(可进推荐依据的 PUB/KB、存在性记录 COM、"
|
|
|
|
|
+ + "「未核实」标注),当前未落地。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.gate.evidenceIsolate] " + fieldName + " 常量值不应为 null。");
|
|
|
|
|
+ assertTrue(expected.equals(String.valueOf(v)),
|
|
|
|
|
+ "[guhe.gate.evidenceIsolate] " + fieldName + " 应取值为 \"" + expected
|
|
|
|
|
+ + "\",实际为 \"" + v + "\"。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.gate.evidenceIsolate] 读取 " + fieldName + " 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 按方法名 + 参数个数探测门禁方法是否暴露(缺失即失败(红),GREEN 落地后继续走行为断言)。 */
|
|
|
|
|
+ private static void assertMethodExists(String methodName, int paramCount) {
|
|
|
|
|
+ boolean found = false;
|
|
|
|
|
+ for (Method m : AttributionComplianceGate.class.getDeclaredMethods()) {
|
|
|
|
|
+ if (m.getName().equals(methodName) && m.getParameterCount() == paramCount) {
|
|
|
|
|
+ found = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(found,
|
|
|
|
|
+ "[guhe.gate.evidenceIsolate] AttributionComplianceGate 缺少 " + methodName
|
|
|
|
|
+ + "(" + paramCount + " 参) 方法:T11(G-3)证据码隔离契约要求门禁暴露该确定性方法,"
|
|
|
|
|
+ + "当前未落地(仅 G-1 的 enforceLowSide / scan / merge 已实现)。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 按方法名 + 参数个数匹配并调用(宽松适配基本/包装类型),契约方法缺失即断言失败。 */
|
|
|
|
|
+ private static Object call(Object host, String method, Object... args) {
|
|
|
|
|
+ for (Method m : host.getClass().getMethods()) {
|
|
|
|
|
+ if (m.getName().equals(method) && m.getParameterCount() == args.length) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return m.invoke(host, args);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ fail("[guhe.gate.evidenceIsolate] 契约方法 "
|
|
|
|
|
+ + host.getClass().getSimpleName() + "#" + method + "(" + args.length + " 参) 调用失败:"
|
|
|
|
|
+ + e.getMessage());
|
|
|
|
|
+ throw new IllegalStateException("unreachable");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ fail("[guhe.gate.evidenceIsolate] 契约方法 " + host.getClass().getSimpleName() + "#"
|
|
|
|
|
+ + method + " 未实现(G-3 证据码隔离未落地,T11)");
|
|
|
|
|
+ throw new IllegalStateException("unreachable");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static boolean callBoolean(Object host, String method, Object... args) {
|
|
|
|
|
+ Object r = call(host, method, args);
|
|
|
|
|
+ if (r instanceof Boolean) {
|
|
|
|
|
+ return (Boolean) r;
|
|
|
|
|
+ }
|
|
|
|
|
+ fail("[guhe.gate.evidenceIsolate] 契约方法 " + method + " 应返回布尔,实际 " + r);
|
|
|
|
|
+ throw new IllegalStateException("unreachable");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ private static List<String> callListString(Object host, String method, Object... args) {
|
|
|
|
|
+ Object r = call(host, method, args);
|
|
|
|
|
+ if (r instanceof List) {
|
|
|
|
|
+ return (List<String>) r;
|
|
|
|
|
+ }
|
|
|
|
|
+ fail("[guhe.gate.evidenceIsolate] 契约方法 " + method + " 应返回 List,实际 " + r);
|
|
|
|
|
+ throw new IllegalStateException("unreachable");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Field declaredField(Class<?> clazz, String name) {
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().equals(name)) {
|
|
|
|
|
+ f.setAccessible(true);
|
|
|
|
|
+ return f;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|