package dev.caskeleton.adapter.outbound.persistence.notification; import dev.caskeleton.adapter.outbound.persistence.platform.JpaPlatformContractSupport; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; /** Analysis-only probe: what each hand-maintained notification migration ladder actually builds. */ @Tag("jpa-contract") class AnalysisNotificationLadderProbe { private static final List NOTIFICATION = List.of( "V1__notification_platform_core.sql", "V2__notification_platform_contact_template_policy.sql", "V3__notification_platform_inbox_admin.sql", "V4__notification_platform_alignment_and_activation.sql", "V5__notification_recipient_expiry.sql", "V6__notification_projection_facts.sql", "V7__notification_collapse.sql", "V8__notification_admin_claim.sql", "V9__notification_evidence_certainty.sql", "V10__variables_payload_envelope_guard.sql"); @Test void ladderColumnCoverage() throws Exception { Set full = null; for (int depth : new int[] {6, 8, 9, 10}) { Set columns = columnsAfter(depth); if (depth == 10) { full = columns; } System.out.println("ladder.notificationVersions=" + depth + " columns=" + columns.size()); } for (int depth : new int[] {6, 8, 9}) { Set columns = columnsAfter(depth); TreeSet missing = new TreeSet<>(full); missing.removeAll(columns); System.out.println( "ladder.notificationVersions=" + depth + " missingVersusV10=" + missing.size()); System.out.println("ladder." + depth + ".missing=" + missing); } } private static Set columnsAfter(int notificationDepth) throws Exception { try (JpaPlatformContractSupport server = JpaPlatformContractSupport.start()) { List statements = new ArrayList<>(); statements.add(read("jpa/core", "V1__initialize_or_adopt.sql")); statements.add(read("jpa/core", "V2__widen_capability_schema_stream.sql")); for (int index = 0; index < notificationDepth; index++) { statements.add(read("jpa/notification-platform", NOTIFICATION.get(index))); } try (Connection connection = server.connection(); Statement statement = connection.createStatement()) { for (String sql : statements) { statement.execute(sql); } } return notificationColumns(server); } } private static Set notificationColumns(JpaPlatformContractSupport server) throws SQLException { TreeSet columns = new TreeSet<>(); try (Connection connection = server.connection(); Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery( "select table_name || '.' || column_name from information_schema.columns" + " where table_schema = 'public' and table_name like 'notification%'" + " order by 1")) { while (rows.next()) { columns.add(rows.getString(1)); } } return columns; } private static String read(String stream, String fileName) throws IOException { try (InputStream resource = AnalysisNotificationLadderProbe.class .getClassLoader() .getResourceAsStream("db/migration/" + stream + "/" + fileName)) { if (resource == null) { throw new IOException("missing " + fileName); } return new String(resource.readAllBytes(), StandardCharsets.UTF_8); } } }