149 lines
5.9 KiB
Java
149 lines
5.9 KiB
Java
package com.ruoyi.shadow.config;
|
|
|
|
import com.ruoyi.shadow.startup.ShadowDatabaseIdentity;
|
|
import com.zaxxer.hikari.HikariDataSource;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
|
|
|
import javax.sql.DataSource;
|
|
import java.sql.Connection;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.never;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
|
|
class ShadowDataSourceIsolationTest {
|
|
@Test
|
|
void sourcePoolIsReadOnlyBoundedAndFailsFast() {
|
|
ShadowProperties properties = properties();
|
|
ShadowSourceConfiguration configuration =
|
|
new ShadowSourceConfiguration();
|
|
|
|
HikariDataSource source = (HikariDataSource)
|
|
configuration.shadowSourceDataSource(properties);
|
|
try {
|
|
assertTrue(source.isReadOnly());
|
|
assertEquals(2, source.getMaximumPoolSize());
|
|
assertEquals(0, source.getMinimumIdle());
|
|
assertEquals(2_000L, source.getConnectionTimeout());
|
|
assertEquals("SET TRANSACTION ISOLATION LEVEL READ COMMITTED",
|
|
source.getConnectionInitSql());
|
|
|
|
JdbcTemplate jdbc = configuration.shadowSourceJdbc(source);
|
|
assertEquals(3, jdbc.getQueryTimeout());
|
|
|
|
DataSourceTransactionManager tx =
|
|
configuration.shadowSourceTx(source);
|
|
assertTrue(tx.isEnforceReadOnly());
|
|
} finally {
|
|
source.close();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void sourceAndEvidenceUseDifferentPoolsAndTransactionManagers() {
|
|
ShadowProperties properties = properties();
|
|
ShadowSourceConfiguration sourceConfiguration =
|
|
new ShadowSourceConfiguration();
|
|
ShadowEvidenceConfiguration evidenceConfiguration =
|
|
new ShadowEvidenceConfiguration();
|
|
|
|
HikariDataSource source = (HikariDataSource)
|
|
sourceConfiguration.shadowSourceDataSource(properties);
|
|
HikariDataSource evidence = (HikariDataSource)
|
|
evidenceConfiguration.shadowEvidenceDataSource(properties);
|
|
try {
|
|
assertNotSame(source, evidence);
|
|
assertTrue(source.isReadOnly());
|
|
assertFalse(evidence.isReadOnly());
|
|
assertNotSame(sourceConfiguration.shadowSourceTx(source),
|
|
evidenceConfiguration.shadowEvidenceTx(evidence));
|
|
} finally {
|
|
source.close();
|
|
evidence.close();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void sourceReadOnlyTransactionDoesNotEmitUnsupportedSqlServerSyntax()
|
|
throws Exception {
|
|
DataSource source = mock(DataSource.class);
|
|
Connection connection = mock(Connection.class);
|
|
when(source.getConnection()).thenReturn(connection);
|
|
when(connection.getAutoCommit()).thenReturn(true);
|
|
|
|
DataSourceTransactionManager tx =
|
|
new ShadowSourceConfiguration().shadowSourceTx(source);
|
|
TransactionTemplate transaction = new TransactionTemplate(tx);
|
|
transaction.setReadOnly(true);
|
|
|
|
transaction.execute(status -> null);
|
|
|
|
verify(connection).setReadOnly(true);
|
|
verify(connection, never()).createStatement();
|
|
}
|
|
|
|
@Test
|
|
void sourceAndEvidenceCannotResolveToTheSamePhysicalDatabase() {
|
|
ShadowDatabaseIdentity source = ShadowDatabaseIdentity.fromJdbcUrl(
|
|
"jdbc:sqlserver://prod-db:1433;databaseName=FT546-wcs",
|
|
"ft546_shadow_ro");
|
|
ShadowDatabaseIdentity evidence = ShadowDatabaseIdentity.fromJdbcUrl(
|
|
"jdbc:sqlserver://PROD-DB:1433;databaseName=ft546-WCS",
|
|
"ft546_shadow_evidence");
|
|
|
|
IllegalStateException failure = assertThrows(
|
|
IllegalStateException.class,
|
|
() -> ShadowDatabaseIdentity.requirePhysicallyDistinct(
|
|
source, evidence));
|
|
|
|
assertEquals("SOURCE_AND_EVIDENCE_DATABASE_MUST_DIFFER",
|
|
failure.getMessage());
|
|
}
|
|
|
|
@Test
|
|
void distinctDatabasesOnTheSameServerAreAccepted() {
|
|
ShadowDatabaseIdentity source = ShadowDatabaseIdentity.fromJdbcUrl(
|
|
"jdbc:sqlserver://prod-db:1433;databaseName=FT546-wcs",
|
|
"ft546_shadow_ro");
|
|
ShadowDatabaseIdentity evidence = ShadowDatabaseIdentity.fromJdbcUrl(
|
|
"jdbc:sqlserver://prod-db:1433;databaseName=FT546-shadow",
|
|
"ft546_shadow_evidence");
|
|
|
|
ShadowDatabaseIdentity.requirePhysicallyDistinct(source, evidence);
|
|
}
|
|
|
|
@Test
|
|
void jdbcIdentityRequiresAnExplicitServerName() {
|
|
IllegalStateException failure = assertThrows(
|
|
IllegalStateException.class,
|
|
() -> ShadowDatabaseIdentity.fromJdbcUrl(
|
|
"jdbc:sqlserver://;databaseName=FT546-wcs",
|
|
"ft546_shadow_ro"));
|
|
|
|
assertEquals("JDBC_SERVER_NAME_REQUIRED", failure.getMessage());
|
|
}
|
|
|
|
private static ShadowProperties properties() {
|
|
ShadowProperties properties = new ShadowProperties();
|
|
properties.getSource().setJdbcUrl(
|
|
"jdbc:h2:mem:source;DB_CLOSE_DELAY=-1");
|
|
properties.getSource().setUsername("source_user");
|
|
properties.getSource().setPassword("source_password");
|
|
properties.getEvidence().setJdbcUrl(
|
|
"jdbc:h2:mem:evidence;DB_CLOSE_DELAY=-1");
|
|
properties.getEvidence().setUsername("evidence_user");
|
|
properties.getEvidence().setPassword("evidence_password");
|
|
return properties;
|
|
}
|
|
}
|