test: add postgres migration tests
This commit is contained in:
1 parent
f3e095a20e
commit
9002c912c0
1 file changed
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package com.slotline.migration;
|
||||||
|
|
||||||
|
import org.flywaydb.core.Flyway;
|
||||||
|
import org.junit.jupiter.api.Assumptions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.testcontainers.DockerClientFactory;
|
||||||
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class PostgresMigrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void migrationsApplyAndPartialUniqueIndexKeepsOneActiveSlot() throws Exception {
|
||||||
|
Assumptions.assumeTrue(DockerClientFactory.instance().isDockerAvailable(),
|
||||||
|
"Docker is not available; skipping the PostgreSQL migration test");
|
||||||
|
|
||||||
|
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")) {
|
||||||
|
postgres.start();
|
||||||
|
|
||||||
|
Flyway flyway = Flyway.configure()
|
||||||
|
.dataSource(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword())
|
||||||
|
.locations("classpath:db/migration/postgresql")
|
||||||
|
.load();
|
||||||
|
flyway.migrate();
|
||||||
|
|
||||||
|
try (Connection connection = DriverManager.getConnection(
|
||||||
|
postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword())) {
|
||||||
|
|
||||||
|
long organizationId = single(connection, "SELECT id FROM organization WHERE slug = 'default'");
|
||||||
|
long userId = returningId(connection, "INSERT INTO app_user "
|
||||||
|
+ "(organization_id, username, password_hash, role, created_at) "
|
||||||
|
+ "VALUES (" + organizationId + ", 'pg-user', 'x', 'USER', now()) RETURNING id");
|
||||||
|
long resourceId = returningId(connection, "INSERT INTO resource "
|
||||||
|
+ "(organization_id, name, description, zone_id, buffer_minutes, created_at, version) "
|
||||||
|
+ "VALUES (" + organizationId + ", 'PG court', 'outdoor', 'UTC', 0, now(), 0) RETURNING id");
|
||||||
|
|
||||||
|
long firstReservationId = returningId(connection, activeReservationInsert(resourceId, organizationId,
|
||||||
|
userId));
|
||||||
|
|
||||||
|
boolean duplicateRejected = false;
|
||||||
|
try (Statement statement = connection.createStatement()) {
|
||||||
|
statement.execute(activeReservationInsert(resourceId, organizationId, userId));
|
||||||
|
} catch (SQLException uniqueViolation) {
|
||||||
|
duplicateRejected = true;
|
||||||
|
}
|
||||||
|
assertThat(duplicateRejected)
|
||||||
|
.as("partial unique index must reject a second ACTIVE reservation on the same slot")
|
||||||
|
.isTrue();
|
||||||
|
|
||||||
|
try (Statement statement = connection.createStatement()) {
|
||||||
|
statement.executeUpdate(
|
||||||
|
"UPDATE reservation SET status = 'CANCELLED' WHERE id = " + firstReservationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean rebookAllowed = true;
|
||||||
|
try (Statement statement = connection.createStatement()) {
|
||||||
|
statement.execute(activeReservationInsert(resourceId, organizationId, userId));
|
||||||
|
} catch (SQLException stillBlocked) {
|
||||||
|
rebookAllowed = false;
|
||||||
|
}
|
||||||
|
assertThat(rebookAllowed)
|
||||||
|
.as("once the slot is cancelled the same slot may be booked again")
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String activeReservationInsert(long resourceId, long organizationId, long userId) {
|
||||||
|
return "INSERT INTO reservation "
|
||||||
|
+ "(resource_id, organization_id, user_id, start_time, end_time, status, created_at, version) "
|
||||||
|
+ "VALUES (" + resourceId + ", " + organizationId + ", " + userId + ", "
|
||||||
|
+ "TIMESTAMPTZ '2026-09-01 10:00:00+00', TIMESTAMPTZ '2026-09-01 11:00:00+00', "
|
||||||
|
+ "'ACTIVE', now(), 0)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private long single(Connection connection, String sql) throws SQLException {
|
||||||
|
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(sql)) {
|
||||||
|
rows.next();
|
||||||
|
return rows.getLong(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long returningId(Connection connection, String sql) throws SQLException {
|
||||||
|
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(sql)) {
|
||||||
|
rows.next();
|
||||||
|
return rows.getLong(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user