Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*/
public final class ReadFileFromResources {

private static java.util.function.Supplier<ClassLoader> classLoaderSupplier = ReadFileFromResources.class::getClassLoader;

private ReadFileFromResources() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
Expand Down Expand Up @@ -53,7 +55,7 @@ public static String readFileFromResources(String fileName, Charset charset) {
// Normalize path separators for cross-platform compatibility
String normalizedFileName = fileName.replace('\\', '/');

try (var inputStream = ReadFileFromResources.class.getClassLoader().getResourceAsStream(normalizedFileName)) {
try (var inputStream = classLoaderSupplier.get().getResourceAsStream(normalizedFileName)) {
// Critical fix: Check for null before calling readAllBytes()
if (inputStream == null) {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -101,4 +103,8 @@ private static void validateFileNameSecurity(String fileName) {
}
}
}

static void setClassLoaderSupplier(java.util.function.Supplier<ClassLoader> supplier) {
classLoaderSupplier = supplier != null ? supplier : ReadFileFromResources.class::getClassLoader;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.jfeatures.msg.codegen;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.lenient;

import com.jfeatures.msg.codegen.domain.DBColumn;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -244,6 +246,79 @@ void testExtractParameters_UnknownSqlType_UsesFallbackType() throws SQLException
assertEquals("VARCHAR", result.get(0).jdbcType()); // Default fallback
}

@Test
void testExtractParameters_SqlLongerThanLimit_ThrowsException() {
StringBuilder builder = new StringBuilder("SELECT * FROM big_table WHERE ");
while (builder.length() <= 10_050) {
builder.append("column = ? AND ");
}
String longSql = builder.append("1=1").toString();

IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () ->
extractor.extractParameters(longSql)
);

assertEquals("SQL query too long", exception.getMessage());
}

@Test
void testExtractParameters_ParameterMetadataUnavailable_FallsBackToParsing() throws SQLException {
// Given
String sql = "UPDATE customers SET name = ? WHERE id = ?";

setupParameterMetaData(2, new int[]{Types.VARCHAR, Types.INTEGER});
when(preparedStatement.getParameterMetaData()).thenThrow(new SQLException("metadata not available"));

// When
List<DBColumn> result = extractor.extractParameters(sql);

// Then - fallback parsing should provide at least one where column entry
assertNotNull(result);
assertEquals(2, result.size());
assertEquals("id", result.get(0).columnName());
assertEquals("param2", result.get(1).columnName());
}

@Test
void testExtractParameters_WhereClauseTooLong_ThrowsException() throws Exception {
StringBuilder whereBuilder = new StringBuilder();
while (whereBuilder.length() <= 10_100) {
whereBuilder.append("column").append(whereBuilder.length()).append(" = ? AND ");
}
String longWhereSql = "SELECT * FROM customers WHERE " + whereBuilder.append("1=1").toString();

var method = ParameterMetadataExtractor.class.getDeclaredMethod("extractColumnNamesFromWhereClause", String.class, int.class);
method.setAccessible(true);

InvocationTargetException exception = assertThrows(InvocationTargetException.class, () ->
method.invoke(extractor, longWhereSql, 1)
);

assertThat(exception.getCause())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("WHERE clause too long");
}

@Test
void testExtractParameters_ExtractWhereClauseSqlTooLong_ThrowsException() throws Exception {
StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM customers WHERE ");
while (sqlBuilder.length() <= 10_050) {
sqlBuilder.append("column = ? OR ");
}
sqlBuilder.append("1=1");

var method = ParameterMetadataExtractor.class.getDeclaredMethod("extractWhereClause", String.class);
method.setAccessible(true);

InvocationTargetException exception = assertThrows(InvocationTargetException.class, () ->
method.invoke(extractor, sqlBuilder.toString())
);

assertThat(exception.getCause())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("SQL query too long");
}

@Test
void testExtractParameters_DatabaseConnectionFails_ThrowsSQLException() throws SQLException {
// Given
Expand Down Expand Up @@ -585,4 +660,4 @@ void testExtractParameters_NegativeParameterCount_ReturnsEmptyList() throws SQLE
assertNotNull(result);
assertEquals(0, result.size());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.jfeatures.msg.codegen;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

class UtilityClassConstructorCoverageTest {

@ParameterizedTest
@MethodSource("utilityClasses")
void utilityConstructorsThrowUnsupportedOperationException(Class<?> utilityClass) throws Exception {
Constructor<?> constructor = utilityClass.getDeclaredConstructor();
constructor.setAccessible(true);

InvocationTargetException exception = assertThrows(InvocationTargetException.class, constructor::newInstance);
assertThat(exception.getCause())
.isInstanceOf(UnsupportedOperationException.class);
}

private static Stream<Class<?>> utilityClasses() {
return Stream.of(
com.jfeatures.msg.codegen.GenerateController.class,
com.jfeatures.msg.codegen.GenerateDAO.class,
com.jfeatures.msg.codegen.GenerateDTO.class,
com.jfeatures.msg.codegen.GenerateDatabaseConfig.class,
com.jfeatures.msg.codegen.GenerateDeleteController.class,
com.jfeatures.msg.codegen.GenerateDeleteDAO.class,
com.jfeatures.msg.codegen.GenerateDeleteDTO.class,
com.jfeatures.msg.codegen.GenerateInsertController.class,
com.jfeatures.msg.codegen.GenerateInsertDAO.class,
com.jfeatures.msg.codegen.GenerateInsertDTO.class,
com.jfeatures.msg.codegen.GenerateSpringBootApp.class,
com.jfeatures.msg.codegen.GenerateUpdateController.class,
com.jfeatures.msg.codegen.GenerateUpdateDAO.class,
com.jfeatures.msg.codegen.GenerateUpdateDTO.class,
com.jfeatures.msg.codegen.sql.SqlParameterReplacer.class,
com.jfeatures.msg.codegen.util.SqlBuilders.class,
com.jfeatures.msg.codegen.util.DtoFieldNameConverter.class,
com.jfeatures.msg.codegen.util.JavaPackageNameBuilder.class,
com.jfeatures.msg.codegen.util.JavaPoetClassNameBuilder.class,
com.jfeatures.msg.codegen.util.JavaPoetTypeNameBuilder.class,
com.jfeatures.msg.codegen.jdbc.JdbcMethodSelector.class,
com.jfeatures.msg.codegen.mapping.ResultSetMappingGenerator.class,
com.jfeatures.msg.codegen.util.SqlStatementDetector.class,
com.jfeatures.msg.sql.ReadFileFromResources.class,
com.jfeatures.msg.codegen.util.ParameterBuilders.class,
com.jfeatures.msg.codegen.util.FieldBuilders.class
);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.jfeatures.msg.codegen.database;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import com.jfeatures.msg.codegen.domain.DatabaseConnection;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.mockito.MockedConstruction;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import com.jfeatures.msg.config.DataSourceConfig;
import com.jfeatures.msg.config.JdbcTemplateConfig;
import com.jfeatures.msg.config.NamedParameterJdbcTemplateConfig;

class DatabaseConnectionFactoryTest {

Expand Down Expand Up @@ -70,6 +79,48 @@ void testCreateDatabaseConnection_ErrorHandling() {
}
}

@Test
void testCreateDatabaseConnection_RuntimeExceptionWrappedAsDatabaseConnectionException() {
try (MockedConstruction<DataSourceConfig> dataSourceConfigConstruction = mockConstruction(DataSourceConfig.class, (mock, context) -> {
javax.sql.DataSource dataSource = mock(javax.sql.DataSource.class);
when(mock.dataSource()).thenReturn(dataSource);
});
MockedConstruction<JdbcTemplateConfig> jdbcTemplateConstruction = mockConstruction(JdbcTemplateConfig.class, (mock, context) -> {
when(mock.jdbcTemplate(any(javax.sql.DataSource.class))).thenThrow(new RuntimeException("boom"));
});
MockedConstruction<NamedParameterJdbcTemplateConfig> namedConstruction = mockConstruction(NamedParameterJdbcTemplateConfig.class)) {

DatabaseConnectionFactory factory = new DatabaseConnectionFactory();
DatabaseConnectionException exception = assertThrows(DatabaseConnectionException.class, factory::createDatabaseConnection);
assertThat(exception)
.hasMessage("Failed to create database connection due to configuration error")
.hasCauseInstanceOf(RuntimeException.class);
}
}

@Test
void testCreateDatabaseConnection_CheckedExceptionWrappedAsDatabaseConnectionException() {
javax.sql.DataSource dataSource = mock(javax.sql.DataSource.class);
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);

try (MockedConstruction<DataSourceConfig> dataSourceConfigConstruction = mockConstruction(DataSourceConfig.class, (mock, context) -> {
when(mock.dataSource()).thenReturn(dataSource);
});
MockedConstruction<JdbcTemplateConfig> jdbcTemplateConstruction = mockConstruction(JdbcTemplateConfig.class, (mock, context) -> {
when(mock.jdbcTemplate(dataSource)).thenReturn(jdbcTemplate);
});
MockedConstruction<NamedParameterJdbcTemplateConfig> namedConstruction = mockConstruction(NamedParameterJdbcTemplateConfig.class, (mock, context) -> {
when(mock.namedParameterJdbcTemplate(dataSource)).thenThrow(new SQLException("failure"));
})) {

DatabaseConnectionFactory factory = new DatabaseConnectionFactory();
DatabaseConnectionException exception = assertThrows(DatabaseConnectionException.class, factory::createDatabaseConnection);
assertThat(exception)
.hasMessage("Failed to create database connection due to unexpected error")
.hasCauseInstanceOf(SQLException.class);
}
}

@Test
void testCreateDatabaseConnection_LoggingBehavior() {
// Test that the factory logs appropriate messages
Expand Down Expand Up @@ -163,4 +214,4 @@ void testCreateDatabaseConnection_ComponentIntegration() {
assertNotNull(e.getMessage(), "Failure should have meaningful message");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.jfeatures.msg.codegen.dbmetadata.ColumnMetadata;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import java.lang.reflect.Method;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -331,11 +332,20 @@ void testSimpleClassNames() {
assertTrue(result.contains("SimpleDTO"));
}

@Test
void capitalizeHandlesNullAndEmptyValues() throws Exception {
Method method = ResultSetMappingGenerator.class.getDeclaredMethod("capitalize", String.class);
method.setAccessible(true);

assertNull(method.invoke(null, (String) null));
assertEquals("", method.invoke(null, ""));
}

private ColumnMetadata createColumn(String columnName) {
ColumnMetadata column = new ColumnMetadata();
column.setColumnName(columnName);
column.setColumnTypeName("VARCHAR");
column.setColumnType(Types.VARCHAR);
return column;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,16 @@ void shouldCreatePublicDtoField() {
assertThat(field.javadoc.toString()).isEmpty(); // Public DTO fields don't have JavaDoc
}

@Test
void shouldUseAliasForPublicDtoField() {
ColumnMetadata metadata = createColumnMetadata("customer_id", "bigint", false);
metadata.setColumnAlias("id_alias");

FieldSpec field = FieldBuilders.publicDtoField(metadata);

assertThat(field.name).isEqualTo("id_alias");
}

@Test
void shouldThrowExceptionForNullColumnMetadataInDtoField() {
assertThrows(IllegalArgumentException.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mockStatic;

import com.jfeatures.msg.codegen.SQLServerDataTypeEnum;
import com.jfeatures.msg.codegen.dbmetadata.ColumnMetadata;
import com.jfeatures.msg.codegen.domain.DBColumn;
import com.squareup.javapoet.ClassName;
Expand All @@ -14,6 +16,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.MockedStatic;

/**
* Comprehensive tests for ParameterBuilders utility class to achieve 90%+ coverage.
Expand Down Expand Up @@ -549,6 +552,21 @@ void shouldHandlePrimitiveTypeBoxingInColumnMetadata() {
assertParameter(parameters.get(0), "countValue", ClassName.get(Integer.class));
}

@Test
void shouldBoxPrimitiveTypesReturnedFromTypeLookup() {
ColumnMetadata column = createColumnMetadata("primitive_flag", "bit");

try (MockedStatic<SQLServerDataTypeEnum> mocked = mockStatic(SQLServerDataTypeEnum.class)) {
mocked.when(() -> SQLServerDataTypeEnum.getClassForType("bit")).thenReturn(boolean.class);

List<ColumnMetadata> columns = List.of(column);
List<ParameterSpec> parameters = ParameterBuilders.fromColumnMetadata(columns, false);

assertThat(parameters).hasSize(1);
assertParameter(parameters.get(0), "primitiveFlag", ClassName.get(Boolean.class));
}
}

private static void assertParameter(
ParameterSpec parameter,
String expectedName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,22 @@ void shouldReturnUnknownForUnsupportedStatement() throws Exception {
assertThat(SqlStatementDetector.detectStatementType("DROP TABLE customer"))
.isEqualTo(SqlStatementType.UNKNOWN);
}
}

@Test
void shouldFallbackToUpdateWhenParsingFails() throws Exception {
assertThat(SqlStatementDetector.detectStatementType("UPDATE broken set"))
.isEqualTo(SqlStatementType.UPDATE);
}

@Test
void shouldFallbackToInsertWhenParsingFails() throws Exception {
assertThat(SqlStatementDetector.detectStatementType("INSERT broken values"))
.isEqualTo(SqlStatementType.INSERT);
}

@Test
void shouldFallbackToDeleteWhenParsingFails() throws Exception {
assertThat(SqlStatementDetector.detectStatementType("DELETE broken"))
.isEqualTo(SqlStatementType.DELETE);
}
}
Loading