Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nextOrThrow methods to KiwiJdbc to advance a ResultSet #1077

Merged
merged 1 commit into from
Nov 18, 2023
Merged
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
48 changes: 48 additions & 0 deletions src/main/java/org/kiwiproject/jdbc/KiwiJdbc.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.kiwiproject.jdbc;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.kiwiproject.base.KiwiPreconditions.checkArgumentNotNull;
import static org.kiwiproject.base.KiwiStrings.format;

import lombok.experimental.UtilityClass;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.kiwiproject.base.KiwiPrimitives;
import org.kiwiproject.base.KiwiPrimitives.BooleanConversionOption;
import org.kiwiproject.base.KiwiStrings;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand All @@ -29,6 +32,51 @@
@UtilityClass
public class KiwiJdbc {

/**
* Attempt to call {@link ResultSet#next()} on the given ResultSet, and throw an {@link IllegalStateException}
* if the result set was not advanced.
*
* @param rs the ResultSet
* @throws SQLException if a database access error occurs or this method is called on a closed result set
* (copied from {@link ResultSet#next()})
* @throws IllegalStateException if the result set was not advanced
*/
public static void nextOrThrow(ResultSet rs) throws SQLException {
nextOrThrow(rs, "ResultSet.next() returned false");
}

/**
* Attempt to call {@link ResultSet#next()} on the given ResultSet, and throw an {@link IllegalStateException}
* if the result set was not advanced.
*
* @param rs the ResultSet
* @param message the error message in case the result set cannot be advanced
* @throws SQLException if a database access error occurs or this method is called on a closed result set
* (copied from {@link ResultSet#next()})
* @throws IllegalStateException if the result set was not advanced
*/
public static void nextOrThrow(ResultSet rs, String message) throws SQLException {
checkState(rs.next(), message);
}

/**
* Attempt to call {@link ResultSet#next()} on the given ResultSet, and throw an {@link IllegalStateException}
* if the result set was not advanced.
*
* @param rs the ResultSet
* @param messageTemplate the error message template in case the result set cannot be advanced, according to how
* {@link KiwiStrings#format(String, Object...)} handles placeholders
* @param args the arguments to be substituted into the message template
* @throws SQLException if a database access error occurs or this method is called on a closed result set
* (copied from {@link ResultSet#next()})
* @throws IllegalStateException if the result set was not advanced
*/
public static void nextOrThrow(ResultSet rs, String messageTemplate, Object... args) throws SQLException {
if (!rs.next()) {
throw new IllegalStateException(format(messageTemplate, args));
}
}

/**
* Convert the timestamp column given by {@code columnName} in the {@link ResultSet} to milliseconds
* from the epoch.
Expand Down
82 changes: 82 additions & 0 deletions src/test/java/org/kiwiproject/jdbc/KiwiJdbcTest.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package org.kiwiproject.jdbc;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -37,6 +41,84 @@
@DisplayName("KiwiJdbc")
class KiwiJdbcTest {

@Nested
class NextOrThrow {

private ResultSet resultSet;

@BeforeEach
void setUp() {
resultSet = mock(ResultSet.class);
}

@Test
void shouldAdvanceResultSet() throws SQLException {
when(resultSet.next()).thenReturn(true);

assertThatCode(() -> KiwiJdbc.nextOrThrow(resultSet))
.doesNotThrowAnyException();

verify(resultSet, only()).next();
}

@Test
void shouldAdvanceResultSet_WithCustomMessageArgument() throws SQLException {
when(resultSet.next()).thenReturn(true);

assertThatCode(() -> KiwiJdbc.nextOrThrow(resultSet, "failed to advance ResultSet"))
.doesNotThrowAnyException();

verify(resultSet, only()).next();
}

@Test
void shouldAdvanceResultSet_WithCustomMessageTemplateAndArguments() throws SQLException {
when(resultSet.next()).thenReturn(true);

assertThatCode(() -> KiwiJdbc.nextOrThrow(resultSet, "{} with id {} was not found", "Order", "12345ABC"))
.doesNotThrowAnyException();

verify(resultSet, only()).next();
}

@Test
void shouldThrowIllegalState_WhenNextReturnsFalse() throws SQLException {
when(resultSet.next()).thenReturn(false);

assertThatIllegalStateException()
.isThrownBy(() -> KiwiJdbc.nextOrThrow(resultSet))
.withMessage("ResultSet.next() returned false");

verify(resultSet, only()).next();
}

@Test
void shouldThrowIllegalState_WithCustomMessage_WhenNextReturnsFalse() throws SQLException {
when(resultSet.next()).thenReturn(false);

assertThatIllegalStateException()
.isThrownBy(() -> KiwiJdbc.nextOrThrow(resultSet, "record was not found"))
.withMessage("record was not found");

verify(resultSet, only()).next();
}

@ParameterizedTest
@ValueSource(strings = {
"{} with id {} was not found",
"%s with id %s was not found"
})
void shouldThrowIllegalState_WithCustomMessageTemplate_WhenNextReturnsFalse(String template) throws SQLException {
when(resultSet.next()).thenReturn(false);

assertThatIllegalStateException()
.isThrownBy(() -> KiwiJdbc.nextOrThrow(resultSet, template, "Item", 42))
.withMessage("Item with id 42 was not found");

verify(resultSet, only()).next();
}
}

@Nested
class EpochMillisFromTimestamp {

Expand Down