diff --git a/client-java/instrumentation/pom.xml b/client-java/instrumentation/pom.xml index 961c2f818b..393daed83d 100644 --- a/client-java/instrumentation/pom.xml +++ b/client-java/instrumentation/pom.xml @@ -67,6 +67,12 @@ ${springboot.version} test + + org.springframework.boot + spring-boot-starter-data-cassandra + ${springboot.version} + test + org.springframework.boot spring-boot-starter-data-redis diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java index 9493cfb078..cb04f623f9 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java @@ -118,6 +118,8 @@ public StatementDescription(String line, String method) { private final Set mongoCollectionSchemaData = new CopyOnWriteArraySet<>(); + private final Set cassandraTableSchemaData = new CopyOnWriteArraySet<>(); + public Set getSqlInfoData(){ return Collections.unmodifiableSet(executedSqlCommandData); } @@ -150,6 +152,10 @@ public Set getMongoCollectionTypeData(){ return Collections.unmodifiableSet(mongoCollectionSchemaData); } + public Set getCassandraTableTypeData(){ + return Collections.unmodifiableSet(cassandraTableSchemaData); + } + public void addSqlInfo(ExecutedSqlCommand info){ executedSqlCommandData.add(info); } @@ -182,6 +188,10 @@ public void addMongoCollectionType(MongoCollectionSchema mongoCollectionSchema){ mongoCollectionSchemaData.add(mongoCollectionSchema); } + public void addCassandraTableType(CassandraTableSchema cassandraTableSchema){ + cassandraTableSchemaData.add(cassandraTableSchema); + } + public Set getParsedDtoNamesView(){ return Collections.unmodifiableSet(parsedDtoNames); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/CassandraTableSchema.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/CassandraTableSchema.java new file mode 100644 index 0000000000..e854b35da3 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/CassandraTableSchema.java @@ -0,0 +1,46 @@ +package org.evomaster.client.java.instrumentation; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Schema of rows in a Cassandra table. + */ +public class CassandraTableSchema implements Serializable { + private final String tableName; + private final String tableSchema; + + public CassandraTableSchema(String tableName, String tableSchema) { + this.tableName = tableName; + this.tableSchema = tableSchema; + } + + public String getTableName() { + return tableName; + } + + public String getTableSchema() { + return tableSchema; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CassandraTableSchema that = (CassandraTableSchema) o; + return Objects.equals(tableName, that.tableName) && Objects.equals(tableSchema, that.tableSchema); + } + + @Override + public int hashCode() { + return Objects.hash(tableName, tableSchema); + } + + @Override + public String toString() { + return "CassandraTableSchema{" + + "tableName='" + tableName + '\'' + + ", tableSchema='" + tableSchema + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java index 24f2b9d3e2..0c2b87b038 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java @@ -29,6 +29,7 @@ public static List getList() { new Base64DecoderClassReplacement(), new BooleanClassReplacement(), new ByteClassReplacement(), + new CassandraTemplateClassReplacement(), new CharacterClassReplacement(), new CollectionClassReplacement(), new CqlSessionClassReplacement(), @@ -54,6 +55,7 @@ public static List getList() { new LocalTimeClassReplacement(), new LongClassReplacement(), new MapClassReplacement(), + new MappingCassandraEntityInformationClassReplacement(), new MatcherClassReplacement(), new MessageBodyReaderClassReplacement(), new MethodClassReplacement(), diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacement.java new file mode 100644 index 0000000000..cf670f711e --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacement.java @@ -0,0 +1,105 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; + +import org.evomaster.client.java.instrumentation.CassandraTableSchema; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.Replacement; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyMethodReplacementClass; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.UsageFilter; +import org.evomaster.client.java.instrumentation.object.ClassToSchema; +import org.evomaster.client.java.instrumentation.shared.ReplacementCategory; +import org.evomaster.client.java.instrumentation.shared.ReplacementType; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +/** + * The intention of this replacement is the same as {@link MappingCassandraEntityInformationClassReplacement}: + * retrieve the entity-type-to-table mapping. But that constructor replacement only fires when Spring Data + * instantiates a {@code CassandraRepository}; a SUT calling {@code CassandraTemplate} directly (bypassing + * the repository layer) needs this replacement to have that mapping recorded too. + */ +public class CassandraTemplateClassReplacement extends ThirdPartyMethodReplacementClass { + + private static final CassandraTemplateClassReplacement singleton = new CassandraTemplateClassReplacement(); + + @Override + protected String getNameOfThirdPartyTargetClass() { + return "org.springframework.data.cassandra.core.CassandraTemplate"; + } + + private static final String INSERT_ID = "insert"; + private static final String SELECT_ONE_ID = "selectOneString"; + private static final String SELECT_ID = "selectString"; + + @Replacement(replacingStatic = false, + type = ReplacementType.TRACKER, + id = INSERT_ID, + usageFilter = UsageFilter.ANY, + category = ReplacementCategory.CASSANDRA) + public static T insert(Object cassandraTemplate, T entity) { + try { + addCassandraTableType(cassandraTemplate, entity.getClass()); + + Method insertMethod = getOriginal(singleton, INSERT_ID, cassandraTemplate); + Object result = insertMethod.invoke(cassandraTemplate, entity); + return (T) result; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw (RuntimeException) e.getCause(); + } + } + + @Replacement(replacingStatic = false, + type = ReplacementType.TRACKER, + id = SELECT_ONE_ID, + usageFilter = UsageFilter.ANY, + category = ReplacementCategory.CASSANDRA) + public static T selectOne(Object cassandraTemplate, String cql, Class entityClass) { + try { + addCassandraTableType(cassandraTemplate, entityClass); + + Method selectOneMethod = getOriginal(singleton, SELECT_ONE_ID, cassandraTemplate); + Object result = selectOneMethod.invoke(cassandraTemplate, cql, entityClass); + return (T) result; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw (RuntimeException) e.getCause(); + } + } + + @Replacement(replacingStatic = false, + type = ReplacementType.TRACKER, + id = SELECT_ID, + usageFilter = UsageFilter.ANY, + category = ReplacementCategory.CASSANDRA) + public static List select(Object cassandraTemplate, String cql, Class entityClass) { + try { + addCassandraTableType(cassandraTemplate, entityClass); + + Method selectMethod = getOriginal(singleton, SELECT_ID, cassandraTemplate); + Object result = selectMethod.invoke(cassandraTemplate, cql, entityClass); + return (List) result; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw (RuntimeException) e.getCause(); + } + } + + private static void addCassandraTableType(Object cassandraTemplate, Class entityClass) { + try { + Object tableNameId = cassandraTemplate.getClass().getMethod("getTableName", Class.class) + .invoke(cassandraTemplate, entityClass); + String tableName = (String) tableNameId.getClass().getMethod("asInternal").invoke(tableNameId); + + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(entityClass, true, Collections.emptyList()); + ExecutionTracer.addCassandraTableType(new CassandraTableSchema(tableName, schema)); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingCassandraEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingCassandraEntityInformationClassReplacement.java new file mode 100644 index 0000000000..980206c230 --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingCassandraEntityInformationClassReplacement.java @@ -0,0 +1,79 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; + +import org.evomaster.client.java.instrumentation.CassandraTableSchema; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.Replacement; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyCast; +import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyMethodReplacementClass; +import org.evomaster.client.java.instrumentation.object.ClassToSchema; +import org.evomaster.client.java.instrumentation.shared.ReplacementCategory; +import org.evomaster.client.java.instrumentation.shared.ReplacementType; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; + +/** + * When a Cassandra repository is created in Spring, a CqlSession is used under the hood. + * But information about the type of the repository's rows is not transferred to the session. + * That info is retained on Spring side. + * So the intention of this replacement is to retrieve that type info. + * This will allow us to create and insert rows of the correct type in the table (and the repository). + */ +public class MappingCassandraEntityInformationClassReplacement extends ThirdPartyMethodReplacementClass { + private static final MappingCassandraEntityInformationClassReplacement singleton = new MappingCassandraEntityInformationClassReplacement(); + private static ThreadLocal instance = new ThreadLocal<>(); + + public static final String CONSTRUCTOR_ENTITY_CONVERTER_ID = "constructorEntityConverter"; + + @Override + protected String getNameOfThirdPartyTargetClass() { + return "org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation"; + } + + public static Object consumeInstance() { + Object mappingCassandraEntityInformation = instance.get(); + if (mappingCassandraEntityInformation == null) { + throw new IllegalStateException("No instance to consume"); + } + instance.set(null); + return mappingCassandraEntityInformation; + } + + private static void addInstance(Object x) { + Object mappingCassandraEntityInformation = instance.get(); + if (mappingCassandraEntityInformation != null) { + throw new IllegalStateException("Previous instance was not consumed"); + } + instance.set(x); + } + + @Replacement( + replacingConstructor = true, + type = ReplacementType.TRACKER, + category = ReplacementCategory.CASSANDRA, + id = CONSTRUCTOR_ENTITY_CONVERTER_ID, + castTo = "org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation" + ) + public static void MappingCassandraEntityInformation( + @ThirdPartyCast(actualType = "org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity") Object entity, + @ThirdPartyCast(actualType = "org.springframework.data.cassandra.core.convert.CassandraConverter") Object converter) { + Constructor original = getOriginalConstructor(singleton, CONSTRUCTOR_ENTITY_CONVERTER_ID); + + try { + Object mappingCassandraEntityInformation = original.newInstance(entity, converter); + addInstance(mappingCassandraEntityInformation); + + Object tableNameId = mappingCassandraEntityInformation.getClass().getMethod("getTableName").invoke(mappingCassandraEntityInformation); + String tableName = (String) tableNameId.getClass().getMethod("asInternal").invoke(tableNameId); + + Class rowType = (Class) mappingCassandraEntityInformation.getClass().getMethod("getJavaType").invoke(mappingCassandraEntityInformation); + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(rowType, true, Collections.emptyList()); + + ExecutionTracer.addCassandraTableType(new CassandraTableSchema(tableName, schema)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + +} \ No newline at end of file diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java index 2386227c12..7df7c68478 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java @@ -478,6 +478,12 @@ public static void addMongoCollectionType(MongoCollectionSchema mongoCollectionS } } + public static void addCassandraTableType(CassandraTableSchema cassandraTableSchema){ + if (!executingInitCassandra) { + getCurrentAdditionalInfo().addCassandraTableType(cassandraTableSchema); + } + } + public static void markLastExecutedStatement(String lastLine, String lastMethod) { getCurrentAdditionalInfo().pushLastExecutedStatement(lastLine, lastMethod); diff --git a/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingCassandraEntityOperationsImpl.java b/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingCassandraEntityOperationsImpl.java new file mode 100644 index 0000000000..ac2361084b --- /dev/null +++ b/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingCassandraEntityOperationsImpl.java @@ -0,0 +1,13 @@ +package com.foo.somedifferentpackage.examples.methodreplacement; + +import org.evomaster.client.java.instrumentation.example.cassandra.MappingCassandraEntityOperations; +import org.springframework.data.cassandra.core.convert.CassandraConverter; +import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation; + +public class MappingCassandraEntityOperationsImpl implements MappingCassandraEntityOperations { + @Override + public MappingCassandraEntityInformation callMappingCassandraEntityInformation(CassandraPersistentEntity entity, CassandraConverter converter) { + return new MappingCassandraEntityInformation<>(entity, converter); + } +} \ No newline at end of file diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacementTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacementTest.java new file mode 100644 index 0000000000..3134281b7a --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateClassReplacementTest.java @@ -0,0 +1,170 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; + +import com.datastax.oss.driver.api.core.CqlSession; +import org.evomaster.client.java.instrumentation.AdditionalInfo; +import org.evomaster.client.java.instrumentation.CassandraTableSchema; +import org.evomaster.client.java.instrumentation.object.ClassToSchema; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +public class CassandraTemplateClassReplacementTest { + + private static CqlSession cqlSession; + private static CassandraTemplate cassandraTemplate; + + private static final int CASSANDRA_PORT = 9042; + private static final String CASSANDRA_IMAGE = "cassandra"; + private static final String CASSANDRA_VERSION = "4.1"; + + private static final GenericContainer cassandra = new GenericContainer<>(CASSANDRA_IMAGE + ":" + CASSANDRA_VERSION) + .withExposedPorts(CASSANDRA_PORT) + .waitingFor(Wait.forLogMessage(".*Starting listening for CQL clients.*", 1) + .withStartupTimeout(Duration.ofMinutes(2))); + + private static final String KEYSPACE = "testks"; + private static final String TABLE_NAME = "cassandra_template_test_dto"; + private static final String HOST_NAME = "localhost"; + private static final String DATA_CENTER = "datacenter1"; + + @BeforeAll + static void startCassandra() { + cassandra.start(); + + InetSocketAddress contactPoint = + new InetSocketAddress(HOST_NAME, cassandra.getMappedPort(CASSANDRA_PORT)); + + // Bootstrap session, only to create the keyspace (which doesn't exist yet, so we + // can't bind to it as a default keyspace until after this point) + try (CqlSession bootstrap = CqlSession.builder() + .addContactPoint(contactPoint) + .withLocalDatacenter(DATA_CENTER) + .build()) { + bootstrap.execute("CREATE KEYSPACE IF NOT EXISTS " + KEYSPACE + + " WITH replication = {'class':'SimpleStrategy','replication_factor':1}"); + } + + // CassandraTemplate's convenience methods (insert/select/selectOneById) build CQL + // using just the unqualified table name, so the session needs a default keyspace. + cqlSession = CqlSession.builder() + .addContactPoint(contactPoint) + .withLocalDatacenter(DATA_CENTER) + .withKeyspace(KEYSPACE) + .build(); + + // Setup: call directly on session so it is NOT intercepted by any replacement + cqlSession.execute("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + + " (id uuid PRIMARY KEY, name text, age int)"); + + cassandraTemplate = new CassandraTemplate(cqlSession); + + ExecutionTracer.reset(); + } + + @AfterAll + static void cleanup() { + if (cqlSession != null) { + cqlSession.close(); + } + ExecutionTracer.reset(); + } + + @BeforeEach + void clearTable() { + // Direct call — not intercepted + cqlSession.execute("TRUNCATE " + TABLE_NAME); + ExecutionTracer.reset(); + } + + private static String expectedSchema() { + return ClassToSchema.getOrDeriveSchemaWithItsRef(CassandraTemplateTestDto.class, true, Collections.emptyList()); + } + + private static void assertSingleRecordedTableType() { + List additionalInfoList = ExecutionTracer.exposeAdditionalInfoList(); + assertEquals(1, additionalInfoList.size()); + + Set tableTypes = additionalInfoList.get(0).getCassandraTableTypeData(); + assertEquals(1, tableTypes.size()); + + CassandraTableSchema tableSchema = tableTypes.iterator().next(); + assertEquals(TABLE_NAME, tableSchema.getTableName()); + assertEquals(expectedSchema(), tableSchema.getTableSchema()); + } + + @Test + void testInsert() { + UUID id = UUID.randomUUID(); + CassandraTemplateTestDto dto = new CassandraTemplateTestDto(id, "Alice", 30); + + CassandraTemplateClassReplacement.insert(cassandraTemplate, dto); + + // verify the row was actually persisted, via a direct (untracked) query + CassandraTemplateTestDto persisted = + cassandraTemplate.selectOneById(id, CassandraTemplateTestDto.class); + assertNotNull(persisted); + assertEquals("Alice", persisted.name); + assertEquals(30, persisted.age); + + assertSingleRecordedTableType(); + } + + @Test + void testSelectOne() { + UUID id = UUID.randomUUID(); + // Direct insert — not intercepted + cqlSession.execute("INSERT INTO " + TABLE_NAME + " (id, name, age) VALUES (" + id + ", 'Bob', 25)"); + + CassandraTemplateTestDto retrieved = CassandraTemplateClassReplacement.selectOne( + cassandraTemplate, "SELECT * FROM " + TABLE_NAME + " WHERE id = " + id, CassandraTemplateTestDto.class); + + assertNotNull(retrieved); + assertEquals("Bob", retrieved.name); + assertEquals(25, retrieved.age); + + assertSingleRecordedTableType(); + } + + @Test + void testSelect() { + UUID id1 = UUID.randomUUID(); + UUID id2 = UUID.randomUUID(); + // Direct inserts — not intercepted + cqlSession.execute("INSERT INTO " + TABLE_NAME + " (id, name, age) VALUES (" + id1 + ", 'Carol', 40)"); + cqlSession.execute("INSERT INTO " + TABLE_NAME + " (id, name, age) VALUES (" + id2 + ", 'Dave', 45)"); + + List retrieved = CassandraTemplateClassReplacement.select( + cassandraTemplate, "SELECT * FROM " + TABLE_NAME, CassandraTemplateTestDto.class); + + assertEquals(2, retrieved.size()); + + assertSingleRecordedTableType(); + } + + @Test + void testDirectCallsAreNotTracked() { + UUID id = UUID.randomUUID(); + CassandraTemplateTestDto dto = new CassandraTemplateTestDto(id, "Erin", 22); + + // Calls that bypass the replacement must not appear in the tracker + cassandraTemplate.insert(dto); + + List additionalInfoList = ExecutionTracer.exposeAdditionalInfoList(); + assertEquals(1, additionalInfoList.size()); + assertTrue(additionalInfoList.get(0).getCassandraTableTypeData().isEmpty()); + } +} \ No newline at end of file diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateTestDto.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateTestDto.java new file mode 100644 index 0000000000..d5fca7c10a --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/CassandraTemplateTestDto.java @@ -0,0 +1,25 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; + +import org.springframework.data.cassandra.core.mapping.PrimaryKey; +import org.springframework.data.cassandra.core.mapping.Table; + +import java.util.UUID; + +@Table("cassandra_template_test_dto") +public class CassandraTemplateTestDto { + + @PrimaryKey + public UUID id; + + public String name; + public int age; + + public CassandraTemplateTestDto() { + } + + public CassandraTemplateTestDto(UUID id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } +} diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityInstrumentedTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityInstrumentedTest.java new file mode 100644 index 0000000000..2e6b15bda7 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityInstrumentedTest.java @@ -0,0 +1,60 @@ +package org.evomaster.client.java.instrumentation.example.cassandra; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.foo.somedifferentpackage.examples.methodreplacement.MappingCassandraEntityOperationsImpl; +import org.evomaster.client.java.instrumentation.InstrumentingClassLoader; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.cassandra.core.convert.CassandraConverter; +import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class MappingCassandraEntityInstrumentedTest { + + public static class SomeType { + public String someField; + } + + protected MappingCassandraEntityOperations getInstance() throws Exception { + + InstrumentingClassLoader cl = new InstrumentingClassLoader("com.foo"); + + return (MappingCassandraEntityOperations) + cl.loadClass(MappingCassandraEntityOperationsImpl.class.getName()) + .newInstance(); + } + + @BeforeEach + public void init(){ + ExecutionTracer.reset(); + assertEquals(0 , ExecutionTracer.getNumberOfObjectives()); + } + + @AfterEach + public void checkInstrumentation(){ + assertTrue(ExecutionTracer.getNumberOfObjectives() > 0); + } + + @SuppressWarnings("unchecked") + @Test + public void testConstructor() throws Exception { + MappingCassandraEntityOperations cassandraInstrumented = getInstance(); + + CassandraPersistentEntity entity = mock(CassandraPersistentEntity.class); + when(entity.getType()).thenReturn(SomeType.class); + when(entity.getTableName()).thenReturn(CqlIdentifier.fromCql("some_table")); + + CassandraConverter converter = mock(CassandraConverter.class); + + MappingCassandraEntityInformation mappingCassandraEntityInformation = + cassandraInstrumented.callMappingCassandraEntityInformation(entity, converter); + assertNotNull(mappingCassandraEntityInformation); + } +} \ No newline at end of file diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityOperations.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityOperations.java new file mode 100644 index 0000000000..138ff458bd --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/cassandra/MappingCassandraEntityOperations.java @@ -0,0 +1,9 @@ +package org.evomaster.client.java.instrumentation.example.cassandra; + +import org.springframework.data.cassandra.core.convert.CassandraConverter; +import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation; + +public interface MappingCassandraEntityOperations { + MappingCassandraEntityInformation callMappingCassandraEntityInformation(CassandraPersistentEntity entity, CassandraConverter converter); +} \ No newline at end of file