assert that a new migration got executed

This commit is contained in:
Michael Hoennig 2025-01-28 06:57:24 +01:00
parent ace819f0b6
commit eac8717bb8

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.hs.migration; package net.hostsharing.hsadminng.hs.migration;
import liquibase.Liquibase; import liquibase.Liquibase;
import liquibase.exception.LiquibaseException;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -15,6 +16,8 @@ import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.BEFORE_TEST_CLASS; import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.BEFORE_TEST_CLASS;
@ -22,61 +25,65 @@ import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.BEFORE_TE
// TODO.impl: The reference-SQL-dump-generation needs to be automated // TODO.impl: The reference-SQL-dump-generation needs to be automated
// HOWTO: generate the prod-reference-SQL-dump during a prod-release // HOWTO: generate the prod-reference-SQL-dump during a prod-release
/** Tests, if the Liquibase scripts can be applied to a database ionitialized with schemas /**
and test-data from a previous version. * Tests, if the Liquibase scripts can be applied to a database ionitialized with schemas
* and test-data from a previous version.
<p>The test needs a dump, ideally from the version of the lastest prod-release:</p> *
* <p>The test needs a dump, ideally from the version of the lastest prod-release:</p>
<ol> *
<li>1. clean the database:<br/> * <ol>
<code>pg-sql-reset</code> * <li>1. clean the database:<br/>
* <code>pg-sql-reset</code>
<li>populate the database:</br> *
<code>./gradlew bootRun --args='--spring.profiles.active=only-office'</code> * <li>populate the database:</br>
* <code>./gradlew bootRun --args='--spring.profiles.active=only-office'</code>
<li>create the reference-schema SQL-file with some initializations:</li> *
<pre><code>cat >src/test/resources/db/prod-only-office-schema-with-test-data.sql <<EOF * <li>create the reference-schema SQL-file with some initializations:</li>
-- ================================================================================= * <pre><code>cat >src/test/resources/db/prod-only-office-schema-with-test-data.sql <<EOF
-- Generated reference-SQL-dump (hopefully of latest prod-release). * -- =================================================================================
-- See: net.hostsharing.hsadminng.hs.migration.LiquibaseCompatibilityIntegrationTest * -- Generated reference-SQL-dump (hopefully of latest prod-release).
-- --------------------------------------------------------------------------------- * -- See: net.hostsharing.hsadminng.hs.migration.LiquibaseCompatibilityIntegrationTest
* -- ---------------------------------------------------------------------------------
-- *
-- Explicit pre-initialization because we cannot use \`pg_dump --create ...\` * --
-- because the database is already created by Testcontainers. * -- Explicit pre-initialization because we cannot use \`pg_dump --create ...\`
-- * -- because the database is already created by Testcontainers.
* --
CREATE ROLE postgres; *
* CREATE ROLE postgres;
CREATE ROLE admin; *
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; * CREATE ROLE admin;
CREATE ROLE restricted; * GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; * CREATE ROLE restricted;
* GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted;
EOF *
</code></pre> * EOF
* </code></pre>
<li>add the dump to that reference-schema SQL-file:</p> *
<pre><code>docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump \ * <li>add the dump to that reference-schema SQL-file:</p>
--column-inserts --disable-dollar-quoting -U postgres postgres \ * <pre><code>docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump \
>>src/test/resources/db/prod-only-office-schema-with-test-data.sql * --column-inserts --disable-dollar-quoting -U postgres postgres \
</code></pre> * >>src/test/resources/db/prod-only-office-schema-with-test-data.sql
</ol> * </code></pre>
* </ol>
<p>The generated dump has to be committed to git and will be used in future test-runs *
until it gets replaced at the next release.</p> * <p>The generated dump has to be committed to git and will be used in future test-runs
*/ * until it gets replaced at the next release.</p>
*/
@Tag("importOfficeData") @Tag("importOfficeData")
@DataJpaTest(properties = { @DataJpaTest(properties = {
"spring.liquibase.enabled=false" // @Sql should go first, Liquibase will be initialized programmtically "spring.liquibase.enabled=false" // @Sql should go first, Liquibase will be initialized programmatically
}) })
@ActiveProfiles("only-office") @ActiveProfiles("only-office")
@TestPropertySource(properties = "spring.liquibase.contexts=only-office") @TestPropertySource(properties = "spring.liquibase.contexts=only-office")
@DirtiesContext @DirtiesContext
@Import({ Context.class, JpaAttempt.class, LiquibaseConfig.class}) @Import({ Context.class, JpaAttempt.class, LiquibaseConfig.class })
@Sql(value = "/db/prod-only-office-schema-with-test-data.sql", executionPhase = BEFORE_TEST_CLASS) @Sql(value = "/db/prod-only-office-schema-with-test-data.sql", executionPhase = BEFORE_TEST_CLASS)
public class LiquibaseCompatibilityIntegrationTest extends CsvDataImport { public class LiquibaseCompatibilityIntegrationTest extends CsvDataImport {
private static final String EXPECTED_CHANGESET = "hs-hosting-SCHEMA";
private static int initialChangeSetCount = 0;
@Autowired @Autowired
private DataSource dataSource; private DataSource dataSource;
@ -85,19 +92,34 @@ public class LiquibaseCompatibilityIntegrationTest extends CsvDataImport {
@BeforeEach @BeforeEach
public void setup() throws Exception { public void setup() throws Exception {
assertThatDatabaseIsInitialized();
final var schemas = em.createNativeQuery("SELECT * FROM pg_catalog.pg_tables WHERE schemaname='public'").getResultList(); runLiquibaseMigrations();
assertThat(schemas).hasSize(2);
final var liquibaseScripts = em.createNativeQuery("SELECT * FROM public.databasechangelog").getResultList();
assertThat(liquibaseScripts).hasSize(286);
// Step 2: Run Liquibase migrations
liquibase.update(new liquibase.Contexts(), new liquibase.LabelExpression());
} }
@Test @Test
void test() { void test() {
// FIXME: check if changes got applied final var liquibaseScripts = singleColumnSqlQuery("SELECT id FROM public.databasechangelog");
assertThat(liquibaseScripts).hasSizeGreaterThan(initialChangeSetCount);
assertThat(liquibaseScripts).contains(EXPECTED_CHANGESET);
}
private void assertThatDatabaseIsInitialized() {
final var schemas = singleColumnSqlQuery("SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname='public'");
assertThat(schemas).containsExactly("databasechangelog", "databasechangeloglock");
final var liquibaseScripts = singleColumnSqlQuery("SELECT * FROM public.databasechangelog");
assertThat(liquibaseScripts).hasSizeGreaterThan(285);
assertThat(liquibaseScripts).doesNotContain(EXPECTED_CHANGESET);
initialChangeSetCount = liquibaseScripts.size();
}
private void runLiquibaseMigrations() throws LiquibaseException {
liquibase.update(new liquibase.Contexts(), new liquibase.LabelExpression());
}
private List<String> singleColumnSqlQuery(final String sql) {
//noinspection unchecked
final var rows = (List<Object>) em.createNativeQuery(sql).getResultList();
return rows.stream().map(Objects::toString).toList();
} }
} }