9 小时以前 9bad721754fe8bbe2e5f459d0706e0fefac569f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package cn.iocoder.yudao.module.qcreport.dal.dataobject.version;
 
import com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
 
import java.lang.reflect.Field;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
 
/**
 * Schema 读写兼容性测试。
 * <p>
 * 走的是生产同款读取路径——MyBatis-Plus 的 {@link Jackson3TypeHandler} 配
 * {@code QcReportTemplateVersionDO.schema} 字段,而不是测试自己 new 一个 ObjectMapper,
 * 因为要防的就是「反序列化读取」这条链路:{@code schema_json} 列里存的都是历史 JSON。
 * <p>
 * 契约 1.1 删掉了 dataSources / bindings / styles 三个从未使用的字段,库里已经存着带这三个键的
 * 旧记录。这里守的是「旧 JSON 仍然读得出、且该有的字段一个不少」——出问题的表现是
 * 「老模板点进去报系统异常」,只有存量数据会中招,事后很难查。
 * <p>
 * 它<b>不</b>负责证明 {@code ReportTemplateSchema} 上 {@code @JsonIgnoreProperties} 的必要性:
 * 实测把那行注解去掉本测试照样通过(Jackson 3 默认就忽略未知属性)。
 * 注解的意义写在那个类上,别把这条测试当成它的守卫。
 */
class ReportTemplateSchemaCompatibilityTest {
 
    /** 1.0 时期存进去的 Schema:含已删除的三个字段,components 还是无类型的 Map */
    private static final String LEGACY_JSON = """
            {
              "schemaVersion": "1.0",
              "page": { "size": "A4", "orientation": "portrait", "margin": { "top": 12, "right": 8, "bottom": 12, "left": 8 } },
              "grapes": { "pages": [ { "frames": [] } ] },
              "components": [ { "id": "abc", "index": 0, "qualityType": "quality-table", "attributes": { "data-qc-repeat": "inspectionItems" } } ],
              "dataSources": [],
              "bindings": [],
              "rules": [ { "id": "r1", "name": "实测值在规格内", "scope": "item", "expression": "item.actualValue >= item.lowerLimit", "enabled": true } ],
              "styles": []
            }""";
 
    @Test
    @DisplayName("含已删除字段的旧 Schema 仍能读出来(否则老模板打不开)")
    void readsLegacySchemaWithRemovedFields() throws Exception {
        ReportTemplateSchema schema = parse(LEGACY_JSON);
 
        assertNotNull(schema);
        assertEquals("1.0", schema.getSchemaVersion(), "旧记录的版本号要原样读出来,不能因为版本升级就丢掉");
        assertEquals("A4", schema.getPage().getSize());
        assertEquals(12.0, schema.getPage().getMargin().getTop().doubleValue());
        assertFalse(schema.getGrapes().isEmpty(), "画布数据不能丢,丢了模板就只剩空壳");
 
        List<ReportTemplateSchema.QualityComponentNode> components = schema.getComponents();
        assertEquals(1, components.size());
        assertEquals("quality-table", components.get(0).getQualityType());
        assertEquals(0, components.get(0).getIndex().intValue());
        assertEquals("inspectionItems", components.get(0).getAttributes().get("data-qc-repeat"));
 
        assertEquals(1, schema.getRules().size(), "规则是设计器管不到的字段,读的时候绝不能少");
        Map<String, Object> rule = schema.getRules().get(0);
        assertEquals("item", rule.get("scope"));
        assertEquals("item.actualValue >= item.lowerLimit", rule.get("expression"));
    }
 
    @Test
    @DisplayName("写入再读出:当前格式往返不丢内容")
    void roundTripsCurrentSchema() throws Exception {
        ReportTemplateSchema schema = parse(LEGACY_JSON);
 
        Jackson3TypeHandler handler = handler();
        ReportTemplateSchema reloaded = (ReportTemplateSchema) handler.parse(handler.toJson(schema));
 
        assertEquals(schema.getSchemaVersion(), reloaded.getSchemaVersion());
        assertEquals("quality-table", reloaded.getComponents().get(0).getQualityType());
        assertEquals("item.actualValue >= item.lowerLimit",
                reloaded.getRules().get(0).get("expression"));
        assertEquals(1, reloaded.getRules().size());
    }
 
    @Test
    @DisplayName("缺字段的旧 Schema 也能读:只有 schemaVersion 不报错")
    void readsSchemaMissingOptionalFields() throws Exception {
        ReportTemplateSchema schema = parse("{\"schemaVersion\":\"1.0\"}");
 
        assertEquals("1.0", schema.getSchemaVersion());
        assertNull(schema.getPage());
        assertNull(schema.getGrapes());
    }
 
    /** 走 MyBatis 实例化类型处理器的方式:带字段,genericType 一并带上 */
    private static ReportTemplateSchema parse(String json) throws Exception {
        return (ReportTemplateSchema) handler().parse(json);
    }
 
    private static Jackson3TypeHandler handler() throws NoSuchFieldException {
        Field field = QcReportTemplateVersionDO.class.getDeclaredField("schema");
        return new Jackson3TypeHandler(QcReportTemplateVersionDO.class, field);
    }
 
}