|
|
@@ -22,6 +22,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.LinkedHashSet;
|
|
|
import java.util.List;
|
|
|
@@ -1555,10 +1556,23 @@ class LsyOnboardingServiceTest {
|
|
|
assertEquals(400, ex.getCode());
|
|
|
assertTrue(ex.getMessage().contains("共 26 项"),
|
|
|
"小微商户的必填集是 18 + 8 = 26,实际: " + ex.getMessage());
|
|
|
+ // ★ 把「:」之后的标签清单按「、」**切回来**再比(数量与集合都相等 ⇒ 多重集相等),
|
|
|
+ // 不是逐条 contains:后者挡不住「有一条标签正好是另一条的前缀」——「商户签约负责人」
|
|
|
+ // 会被「商户签约负责人电话 / 邮箱」这两条自己带出来。修复轮 1 的 F1 实测:把 production
|
|
|
+ // 里 contact_name 那条标签改成「签约负责人」,旧断言下本类 57 条**全绿**。
|
|
|
for (String label : MERCHANT_MICRO_MISSING_LABELS) {
|
|
|
- assertTrue(ex.getMessage().contains(label),
|
|
|
- "文案缺中文标签「" + label + "」,实际: " + ex.getMessage());
|
|
|
+ assertFalse(label.contains("、"),
|
|
|
+ "夹具里的标签本身含顿号 ⇒ 按顿号切分清单这个判据不成立,先换切分依据:" + label);
|
|
|
}
|
|
|
+ List<String> actualLabels = labelsIn(ex.getMessage());
|
|
|
+ Set<String> expectedLabels = new LinkedHashSet<>(Arrays.asList(MERCHANT_MICRO_MISSING_LABELS));
|
|
|
+ assertEquals(MERCHANT_MICRO_MISSING_LABELS.length, expectedLabels.size(),
|
|
|
+ "夹具数组自身有重复项 ⇒ 下面的比较不再是「逐字相同」,先修夹具");
|
|
|
+ assertEquals(MERCHANT_MICRO_MISSING_LABELS.length, actualLabels.size(),
|
|
|
+ "文案里的标签个数必须恰好是 " + MERCHANT_MICRO_MISSING_LABELS.length
|
|
|
+ + "(少一个 / 多一个都红),实际: " + actualLabels);
|
|
|
+ assertEquals(expectedLabels, new LinkedHashSet<>(actualLabels),
|
|
|
+ "标签清单必须与期望的 26 个逐字相同(顺序不参与断言),实际: " + actualLabels);
|
|
|
for (String key : new String[]{"merchant_name", "merchant_mcc_code", "account_idcard_num",
|
|
|
"fSettle_account_type", "picture_idcard_hander", "mcht_Landuse"}) {
|
|
|
assertFalse(ex.getMessage().contains(key),
|
|
|
@@ -1571,8 +1585,12 @@ class LsyOnboardingServiceTest {
|
|
|
assertEquals(400, noCategory.getCode());
|
|
|
assertTrue(noCategory.getMessage().contains("共 2 项"),
|
|
|
"类别为空时只有两个派生字段填不出来,实际: " + noCategory.getMessage());
|
|
|
- assertTrue(noCategory.getMessage().contains("商户类型"), noCategory.getMessage());
|
|
|
- assertTrue(noCategory.getMessage().contains("账户类型"), noCategory.getMessage());
|
|
|
+ // 同一根的第二处:这里原本也是 contains ⇒ 同样切成「按顿号切开比集合」。
|
|
|
+ List<String> derivedLabels = labelsIn(noCategory.getMessage());
|
|
|
+ assertEquals(2, derivedLabels.size(),
|
|
|
+ "类别为空时文案里只该有这两个标签,实际: " + derivedLabels);
|
|
|
+ assertEquals(Set.of("商户类型", "账户类型"), new LinkedHashSet<>(derivedLabels),
|
|
|
+ "类别为空时文案里只该有这两个标签(顺序不参与断言),实际: " + derivedLabels);
|
|
|
assertFalse(noCategory.getMessage().contains("商户名称"),
|
|
|
"18 个无条件字段全填了,不该报它们中的任何一个:" + noCategory.getMessage());
|
|
|
|
|
|
@@ -1583,8 +1601,17 @@ class LsyOnboardingServiceTest {
|
|
|
* 小微商户那一类的 26 个必填字段的**中文标签**,顺序照 {@code LsyMerchantRequiredFields}
|
|
|
* 的声明顺序(18 个无条件在前、小微独有的 8 个在后)。
|
|
|
*
|
|
|
- * <p>断言用的是 {@code contains},所以顺序不影响结果 —— 排成这个顺序只为读起来
|
|
|
- * 能跟矩阵那张表逐行对照。
|
|
|
+ * <p>★ 断言是把文案里「:」之后的清单**按「、」切回来**再比(数量与集合都相等 ⇒ 多重集相等),
|
|
|
+ * 所以这个数组的**排列不参与断言** —— 排成矩阵声明顺序只为读起来能跟那张表逐行对照。
|
|
|
+ *
|
|
|
+ * <p>★ 修复轮 1(F1)之前这里用的是逐条 {@code contains},那是**恒真**的:
|
|
|
+ * 数组里「商户签约负责人」是「商户签约负责人电话 / 邮箱」的前缀,改掉前者那条标签文案,
|
|
|
+ * 后两条自己就把前缀带出来了。切分之后「少一个 / 多一个 / 串一个 / 一条是另一条的前缀」
|
|
|
+ * 才会红 —— 这正是一条是另一条前缀时 {@code contains} 唯一抓不到的情形。
|
|
|
+ *
|
|
|
+ * <p>★ 切分这个判据的两个前提(改本数组或改文案构造时要一起看):① 标签里不含顿号「、」
|
|
|
+ * (用例里有断言,含了就红);② 文案的构造是「进件必填项缺失(共 N 项):」+
|
|
|
+ * {@code String.join("、", labels)}({@code LsyOnboardingService} 必填缺失那一支)。
|
|
|
*/
|
|
|
private static final String[] MERCHANT_MICRO_MISSING_LABELS = {
|
|
|
"商户名称",
|
|
|
@@ -1671,13 +1698,22 @@ class LsyOnboardingServiceTest {
|
|
|
* {@code merchant_no}(spec §6.2(c):只有真跑一次才知道哪个是准的)⇒ 两个都读,
|
|
|
* 谁给值就用谁。
|
|
|
*
|
|
|
- * <p>三个子场景:
|
|
|
+ * <p>四个子场景:
|
|
|
* <ol>
|
|
|
* <li>只有示例那个键({@code merchant_no});</li>
|
|
|
* <li>只有参数表那个键({@code shoubei_merchant_no});</li>
|
|
|
- * <li>示例那个键给了**空串**(与没给一样不可用 —— 判据是空白,不是「键在不在」)⇒ 仍要取到参数表那个键。</li>
|
|
|
+ * <li>示例那个键给了**空串**(与没给一样不可用 —— 判据是空白,不是「键在不在」)⇒ 仍要取到参数表那个键;</li>
|
|
|
+ * <li>★ **两个键都在、都是非空、且值不同** ⇒ 取到的是 {@code merchant_no} 那个。</li>
|
|
|
* </ol>
|
|
|
*
|
|
|
+ * <p>★★ 第 4 个子场景是**未决语义下的现状钉子**,不是设计决策:spec §6.2(c) 的立场是两个键
|
|
|
+ * **本该是同义别名**(返回参数表写一个、返回示例写另一个),正常只会有一个有值;
|
|
|
+ * 真出现「两个都有值且不同」时哪个算数**当前没有依据** —— 联调跑一次之前不猜。
|
|
|
+ * 所以这里把**现状**(服务端 {@code createMerchant} 先读 {@code merchant_no}、空白才回落
|
|
|
+ * {@code shoubei_merchant_no})钉住:将来任何人改动那个优先级,这条断言都会红。
|
|
|
+ * <b>语义裁定之后要改两处</b>:① 服务端取号那两行的先后;② 本用例第 4 个子场景的期望值
|
|
|
+ * 与这段 javadoc(钉子本身就是为了让后来人一眼看到该去哪改)。
|
|
|
+ *
|
|
|
* <p>★ 这里读的两个键名是**上游的**,属于内部代码,不触 GC 1(GC 1 只管网页面 / 错误消息 /
|
|
|
* 应答字段名)—— 不要为了「避讳」把它们从解析代码里去掉,那会直接把取号打断。
|
|
|
*/
|
|
|
@@ -1688,7 +1724,10 @@ class LsyOnboardingServiceTest {
|
|
|
{ok(",\"merchant_no\":\"851901070001\""), "851901070001", "返回示例的键"},
|
|
|
{ok(",\"shoubei_merchant_no\":\"851901070002\""), "851901070002", "返回参数表的键"},
|
|
|
{ok(",\"merchant_no\":\"\",\"shoubei_merchant_no\":\"851901070003\""), "851901070003",
|
|
|
- "示例那个键给了空串 ⇒ 与没给一样,仍要取到参数表那个键"}};
|
|
|
+ "示例那个键给了空串 ⇒ 与没给一样,仍要取到参数表那个键"},
|
|
|
+ {ok(",\"merchant_no\":\"851901070004\",\"shoubei_merchant_no\":\"851901070005\""),
|
|
|
+ "851901070004",
|
|
|
+ "★ 两个键都在且值不同 ⇒ 取示例那个键(现状钉子,语义待联调裁定,见 javadoc)"}};
|
|
|
for (String[] c : cases) {
|
|
|
stubUpstream(c[0]);
|
|
|
assertEquals(c[1], service.createMerchant(merchantDto()), c[2]);
|
|
|
@@ -1863,4 +1902,21 @@ class LsyOnboardingServiceTest {
|
|
|
private static List<String> messages(List<ILoggingEvent> events) {
|
|
|
return events.stream().map(ILoggingEvent::getFormattedMessage).toList();
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 把必填缺失文案里「:」之后的标签清单**切回来**({@code "……(共 N 项):甲、乙、丙"} ⇒ {@code [甲,乙,丙]})。
|
|
|
+ *
|
|
|
+ * <p>为什么不是逐条 {@code contains}:{@code contains} 挡不住前缀关系 —— 期望清单里
|
|
|
+ * 「商户签约负责人」恰好是「商户签约负责人电话」的前缀,于是改掉前者文案(甚至删掉它那一项)
|
|
|
+ * 断言照样绿(修复轮 1 的 F1,实测过:改 production 那条标签,本类 57 条全绿)。
|
|
|
+ * 切开来比集合,每个标签才各自独立地被钉住。
|
|
|
+ *
|
|
|
+ * <p>用**第一个**全角冒号切:前缀「进件必填项缺失(共 N 项):」里只有这一个冒号,
|
|
|
+ * 标签里若出现冒号也只会在它之后。
|
|
|
+ */
|
|
|
+ private static List<String> labelsIn(String message) {
|
|
|
+ int colon = message.indexOf(':');
|
|
|
+ assertTrue(colon >= 0, "必填缺失的文案里没有全角冒号「:」,切不出标签清单:" + message);
|
|
|
+ return List.of(message.substring(colon + 1).split("、", -1));
|
|
|
+ }
|
|
|
}
|