spring-boot with spring-security integration tests - spring-security

I have a series of Integration Tests that run fine individually, but as a group they fail with primary key violation.
Here are two example tests:
class AddressPopulatorTest extends BaseSpecification {
#Autowired
private AddressRepository addressRepository
#Autowired
private AddressPopulator addressPopulator;
def "test address list is retrieved from Treasury.gov"() {
when:
def count = addressPopulator.populate();
then:
def sdnList = addressRepository.findAll()
sdnList.size() > 5000
sdnList.size() == count
}
}
and
class SdnPopulatorTest extends BaseSpecification {
#Autowired
private SdnRepository sdnRepository
#Autowired
private SdnPopulator sdnPopulator;
def "test sdn list is retrieved from Treasury.gov"() {
when:
def count = sdnPopulator.populate();
then:
def sdnList = sdnRepository.findAll()
sdnList.size() > 5000
sdnList.size() == count
}
And here is the base class which I was hoping would initialize everything just once:
#ContextConfiguration(classes = MyClass, loader = SpringApplicationContextLoader)
#Transactional
#IntegrationTest
public class BaseSpecification extends Specification {
}
However, each tests appear to be trying to create the database with the schema.sql and populate the security users table with the data.sql file. I thought the base test class would just do this one time, not for every test. My schema.sql can check for existing tables and not create if exists
Here is the exception that happens on each test after the first:
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_4 ON PUBLIC.USERS(USERNAME)"; SQL statement:
INSERT INTO users (username, password, enabled) VALUES ('admin', '$2a$10$GVPAMYRozI08Mcll5too6.Q4M2jTO0iJVoiaVDv9pMxRqTpxNj9vO', TRUE) [23505-172]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
at org.h2.message.DbException.get(DbException.java:169)
at org.h2.message.DbException.get(DbException.java:146)
at org.h2.index.BaseIndex.getDuplicateKeyException(BaseIndex.java:83)
at org.h2.index.TreeIndex.add(TreeIndex.java:65)
at org.h2.table.RegularTable.addRow(RegularTable.java:124)
at org.h2.command.dml.Insert.insertRows(Insert.java:126)
at org.h2.command.dml.Insert.update(Insert.java:86)
at org.h2.command.CommandContainer.update(CommandContainer.java:79)
at org.h2.command.Command.executeUpdate(Command.java:235)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:180)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:155)
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:459)
Is there a way to create all database tables with a schema.sql & data.sql inserts once at the beginning of the tests, as I think that seems to be the problem. Or my approach to this wrong?

Related

Running init script on oracle test container with system privileges

I am struggling with org.testcontainers:oracle-xe:1.14.3.
I am trying to run a test intended to verify schema creation and migration, however I'm getting stuck at the InitScript, when trying to initialize the users for the test with the users 'sys as sysdba'.
#Before
public void setUp() {
oracleContainer = new OracleContainer("oracleinanutshell/oracle-xe-11g")
.withUsername("sys as sysdba")
.withInitScript("oracle-initscript.sql");
oracleContainer.start();
}
The above seems to be able to connect, but execution of the init script fails with a
ORA-01109: database not open
Using the 'system' user in the above does not provide the InitScript connection with sysdba privileges, but result in an open database.
I'm looking for a solution that will allow me to initialize multiple users prior to a test. This initialization has grants that requires sysdba privileges. The test, in which some SQL scripts are executed, requires that both users are created in the database and can connect to the database.
In my case I'm using
oracleContainer = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim")
.withUsername("test")
.withPassword("test")
.addEnv("ORACLE_PASSWORD", "s") // Sys password is required
.withCopyFileToContainer(MountableFile.forHostPath("oracle-initscript.sql"), "/container-entrypoint-initdb.d/init.sql")
gvenzl/oracle-xe is the default image used by the org.testcontainers.oracle-xe library.
The documentation for this image describes how to call initialization SQL on DB start and it works great.
Hard to say what is the issue but here are some tricks:
maybe "sys as sysdba" is not valid in your code, documentation is not clear about the usage
maybe withLogConsumer can provide some clues what's wrong
I recommend the image gvenzl/oracle-xe,
in some cases withInitScript may not work properly.
it is useful to test the init script on the container started manually
I finished on end with this approach:
as sys admin created two different schema/user)
#SpringBootTest(classes = Main.class)
#Import(DbConfiguration.class)
#Testcontainers
public class ServiceIntegrationTest {
#Container
public static final OracleContainer oracleContainer =
new OracleContainer("gvenzl/oracle-xe:21-slim-faststart");
}
import static com.integrationtests.local_test.service.IntegrationTest.oracleContainer;
#TestConfiguration
public class DbConfiguration {
static final String DEFAULT_SYS_USER = "sys as sysdba";
private static final String ENTITY_MANAGER_FACTORY = "entityManagerFactory";
#Bean
public DataSource getDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("oracle.jdbc.OracleDriver");
dataSourceBuilder.url(oracleContainer.getJdbcUrl());
dataSourceBuilder.username(DEFAULT_SYS_USER);
dataSourceBuilder.password(oracleContainer.getPassword());
return dataSourceBuilder.build();
}
Also in application.yaml put scripts
spring:
datasource:
initialization-mode: always
schema:
- classpath:/sql/init_schemas/USER_ONE.sql
- classpath:/sql/init_schemas/USER_TWOT.sql

SpringData Cassandra Java Adapter - Using UDTs in Map Columns

I am using the 1.5.1 version of the Cassandra adapter for Java, and I am attempting to insert a record to a table having a Map column that uses a UDT as the column value (key is just a varchar). I created the table using cqlsh and am able to insert to it in CQL just fine. When i try to use a CassandraTemplate...insert(), though, all I get is:
UserTypeResolver must not be null
My UDT class is defined as:
#UserDefinedType("iss_type")
public class IssueType {
#CassandraType(type = DataType.Name.VARCHAR)
private String issue_code;
#CassandraType(type = DataType.Name.VARCHAR)
private String issue_name;
#CassandraType(type = DataType.Name.TIMESTAMP)
private Date issue_start;
#CassandraType(type = DataType.Name.TIMESTAMP)
private Date issue_end;
And my table is defined as
#Table
public class IssueMap {
#PrimaryKeyColumn(
type = PrimaryKeyType.PARTITIONED)
private UUID map_id;
#Column
private Map<String, IssueType> issue_map;
In my config, i have:
#Bean
public CassandraMappingContext cassandraMapping()
throws ClassNotFoundException {
BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(),"campaign_management"));
return mappingContext;
}
Is there something I am missing here? Or is it just not possible to define columns this way? I realize I could just use a query builder to create the CQL and do the insert, but I was hoping to be able to make full use of the Cassandra template features.

Do spring data neo4j repositories not distinguish types?

This is a surprise. The Spring-Data-Neo4j GraphRepository code doesn't appear to be able to distinguish between objects of different types.
If I query a repository for an id which is a different type of object I would have expected it not to exist, and not be be loaded - but my test below shows that the repository doesn't appear to be qualifying the exist/findOne call with the object type and so is erroneously loading a B instance as if it's an A.
Is this a bug or a feature?
#NodeEntity class A { #GraphId Long id }
interface ARepository extends GraphRepository<A> {}
#NodeEntity class B { #GraphId Long id }
interface BRepository extends GraphRepository<B> {}
def "objects of different types should not leak between repositories"() {
given: "an A and B object in the neo4j database"
def a = new A()
def b = new B()
aRepository.save(a)
bRepository.save(b)
assert a.id != b.id
when: "the A repository is queried for a B-id"
def exists = aRepository.exists(b.id)
def result = aRepository.findOne(b.id)
then: "it should not be exist, and should not be loaded"
!exists // This assertion fails
!result // This assertion also fails
}
It looks like delete similarly can delete objects of different types. I would have thought that all CRUD method would have been qualified by the label associated with the domain object that the repository is working for.
There is a typesafety strategy/policy in SDN for controlling that behavior:
<bean id="typeSafetyPolicy" class="org.springframework.data.neo4j.support.typesafety.TypeSafetyPolicy">
<constructor-arg type="org.springframework.data.neo4j.support.typesafety.TypeSafetyOption"><value>RETURNS_NULL</value></constructor-arg>
</bean>
or
#Bean TypeSafetyPolicy typeSafetyPolicy() {
return new TypeSafetyPolicy(TypeSafetyOption.RETURNS_NULL);
}

Grails Model Unity Test

I have a "domain"(model) that want to do an Unity Test to check if works.
But when I execute the test I get
java.lang.IllegalArgumentException: Test class can only have one constructor
Thats always happen when trying to initialize a class of some domain(model).
What would be the approach to do correctly the testcase?
class Race {
static constraints = { }
String name
Date startDate
String city
String state
BigDecimal distance
BigDecimal cost
Integer maxRunners = 100000
BigDecimal inMiles() {
return 0.6
}
}
And in the Unity Test Class
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(Race)
class RaceTest extends Specification {
void testInMiles() {
when:
def model = new Race(distance:5.0);
then:
0.6 == model.inMiles()
}
}
In Grails 2.4.x (which is what I'm assuming you're using) the default test type is a Spock test, and that's what's created by the generate-* scripts. You can still write your own tests in JUnit 3 or 4 style if you prefer. But test classes in Spock (at least using the Grails integration, I'm not sure if it's as strict outside of Grails) have to have names ending in "Spec". That's why you're seeing that error.
Test methods do not have to be void and start with "test" (JUnit 3 style) or be void and have an #Test annotation (JUnit 4 style). The test runner decides if a method is a test method if it's public (either explicitly or if there's no scope modifier) and there's at least one labelled block, e.g. when:, given:, then:, etc. Further, Spock uses some AST magic to allow you to use spaces in method names (you just have to quote the whole name) and have expressive, self-descriptive method names, e.g.
def 'an admin without ROLE_SUPER cannot view records of other admins'() {
...
}

Grails integration test cannot invoke method on null service object?

Simple service class, AnalyzerService, calls stored proc in a database.
Attempting to run integration test to ensure service calls the stored proc and correct data is returned after analyzer class operates on it. However, getting the dreaded exception that "Cannot invoke method calculateEstimateNumberOfPositions() on null object". Why is the service object null? What am I missing?
THANK YOU!
package foobar.analyze
import static org.junit.Assert.*
import org.junit.*
import foobar.analyze.AnalyzerService
//#TestFor(AnalyzerService)
class AnalyzerServiceTests {
def AnalyzerService service
def dataSource
#Before
void setUp() { }
#After
void tearDown() { }
#Test
void testcalculateEstimateNumberOfPositions() {
String positionName = "crew"
String city = "Great Neck"
String state = "NY"
int numberOfPositionsSought = 100
int expectedNumberOfPositionsEstimate = 100
def numberOfPositionsEstimate = service.calculateEstimateNumberOfPositions(positionName, city, state, numberOfPositionsSought)
fail (numberOfPositionsEstimate != expectedNumberOfPositionsEstimate)
}
}
Convention. Stick to the convention. Anything out of convention, regarding nomenclature, will create problem during dependency injection.
Convention is to use the service class name as analyzerService instead of service in integration test.
The integration test should look like
class AnalyzerServiceTests extends GroovyTestCase {
//Service class injected only if you
//use the naming convention as below for AnalyzerService
def analyzerService
def dataSource
......
......
}
It was possible to use service in unit test case when you use the test mixin
#TestFor(AnalyzerService)
By using the above in unit test cases, you could use the default service variable in the test cases. This is not the same in case of integration tests.

Resources