package com.ruoyi.config; 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.security.MessageDigest; import java.util.HexFormat; import org.junit.jupiter.api.Test; class FlywayMigrationContractTest { private static final String V1_SHA256 = "37de76546e5aa0aa0ce7e976b73383a2d80ef97fd2db923d9e23dbc2b34534c9"; private static final String V2_SHA256 = "698023958beee6245b099cf4505d35874cd06823e4dbbc3ddcb11b8f76609b89"; private static final String V3_SHA256 = "763de1dfaa96e6f07f7ed3b3975e6f281dbc0c2e7f139da3bed6a2f1bfc39727"; @Test void keepsImmutableBaselineAndAddsObservationIncrementOnly() { assertEquals(V1_SHA256, sha256("db/migration/V1__ruoyi_platform_baseline.sql")); assertEquals(V2_SHA256, sha256("db/migration/V2__battery_detection_domain.sql")); assertEquals(V3_SHA256, sha256("db/migration/V3__battery_detection_permissions.sql")); String v4 = resource("db/migration/V4__observation_persistence_constraints.sql"); assertTrue(v4.contains("ADD COLUMN creator_id")); assertTrue(v4.contains("uk_exception_open_dedupe")); assertTrue(v4.contains("adapter_timezone_id")); assertTrue(v4.contains("fk_sample_channel")); assertFalse(v4.matches("(?s).*\\bINSERT\\s+INTO\\b.*")); String v5 = resource("db/migration/V5__adapter_discovery_runtime.sql"); assertTrue(v5.contains("connection_status")); assertTrue(v5.contains("last_observed_at_utc")); assertTrue(v5.contains("chk_adapter_connection_status")); assertTrue(v5.contains("system:deviceAdapter:list")); assertTrue(v5.contains("system:deviceAdapter:query")); assertTrue(v5.contains("system:deviceAdapter:add")); assertTrue(v5.contains("system:deviceAdapter:edit")); assertTrue(v5.contains("system:deviceAdapter:changeStatus")); assertTrue(v5.contains("system:deviceAdapter:discover")); assertTrue(v5.contains("system:detectionCabinet:list")); assertFalse(v5.matches("(?s).*\\bDELETE\\s+FROM\\b.*")); } private static String sha256(String path) { try { return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") .digest(resource(path).getBytes(StandardCharsets.UTF_8))); } catch (Exception exception) { throw new AssertionError("无法读取迁移资源 " + path, exception); } } private static String resource(String path) { try (InputStream stream = FlywayMigrationContractTest.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); } } }