package com.battery.business.runtime; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.UUID; import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; import org.junit.jupiter.api.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.init.ScriptUtils; /** * 循环记录来源类别的迁移契约:设备没有循环汇总行时循环统计由工步层聚合得出, * 该来源事实必须落在库里,界面与导出才能明确标注"这个数是推导的"。 */ class CycleRecordSourceKindMigrationContractTest { @Test void migrationAddsSourceKindColumnWithDeviceAsHistoricalDefault() throws Exception { String path = "db/migration-final/V23__cycle_record_source_kind.sql"; String migration = resource(path); assertTrue(migration.contains("ADD COLUMN source_kind VARCHAR(16) NOT NULL DEFAULT 'DEVICE'")); assertTrue(migration.contains("AFTER source_record_id")); // 迁移只加列:不改写既有循环事实,也不写入任何数据。 assertFalse(migration.matches("(?s).*\\b(INSERT|UPDATE|DELETE|DROP)\\b.*")); var source = new UnpooledDataSource("org.h2.Driver", "jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DATABASE_TO_LOWER=TRUE", "sa", ""); try (var connection = source.getConnection()) { JdbcTemplate jdbc = new JdbcTemplate(source); jdbc.execute("create table t_test_cycle_record(cycle_record_id bigint auto_increment primary key," + "source_record_id varchar(128))"); // 迁移之前写入的循环记录按设备事实处理:语言上的默认值与存量一致。 jdbc.update("insert into t_test_cycle_record(source_record_id) values(?)", "CYCLE-1"); ScriptUtils.executeSqlScript(connection, new ClassPathResource(path)); assertEquals("DEVICE", jdbc.queryForObject( "select source_kind from t_test_cycle_record where source_record_id = 'CYCLE-1'", String.class)); // 聚合来源是显式写入的事实,不是从别处推断出来的。 jdbc.update("insert into t_test_cycle_record(source_record_id, source_kind) values(?,?)", "neware:442:cycle:3", "AGGREGATED"); assertEquals(1, jdbc.queryForObject( "select count(*) from t_test_cycle_record where source_kind = 'AGGREGATED'", Integer.class)); } } private static String resource(String path) { try (InputStream stream = CycleRecordSourceKindMigrationContractTest.class.getClassLoader() .getResourceAsStream(path)) { if (stream == null) throw new AssertionError("缺少资源 " + path); return new String(stream.readAllBytes(), StandardCharsets.UTF_8); } catch (Exception exception) { throw new AssertionError("无法读取资源 " + path, exception); } } }