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

#44: Improved assertion message when more columns expected than present. #47

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
50 changes: 20 additions & 30 deletions dependencies.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/changes/changelog.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions doc/changes/changes_1.6.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Matcher for SQL Result Sets 1.6.2, released 2023-10-27

Code name: Fix expectation with too many columns

## Summary

We fixed an issue where expecting more columns than are actually in the result set would throw an `ArrayIndexOutOfBoundsException`.

## Features

* #44: Fixed `ArrayIndexOutOfBoundsException` when expecting more columns than are in the result set.

## Dependency Updates

### Test Dependency Updates

* Updated `org.testcontainers:jdbc:1.19.0` to `1.19.1`
* Updated `org.testcontainers:junit-jupiter:1.19.0` to `1.19.1`

### Plugin Dependency Updates

* Updated `com.exasol:error-code-crawler-maven-plugin:1.3.0` to `1.3.1`
* Updated `com.exasol:project-keeper-maven-plugin:2.9.12` to `2.9.14`
* Updated `org.apache.maven.plugins:maven-enforcer-plugin:3.4.0` to `3.4.1`
* Updated `org.apache.maven.plugins:maven-javadoc-plugin:3.5.0` to `3.6.0`
* Updated `org.codehaus.mojo:versions-maven-plugin:2.16.0` to `2.16.1`
* Updated `org.jacoco:jacoco-maven-plugin:0.8.10` to `0.8.11`
* Updated `org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184` to `3.10.0.2594`
17 changes: 9 additions & 8 deletions pk_generated_parent.pom

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>hamcrest-resultset-matcher</artifactId>
<version>1.6.1</version>
<version>1.6.2</version>
<name>Matcher for SQL Result Sets</name>
<description>This project provides hamcrest matcher that compares java.sql.ResultSet objects.</description>
<url>https://github.com/exasol/hamcrest-resultset-matcher/</url>
Expand Down Expand Up @@ -30,7 +30,8 @@
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.15.2.0</version> <!-- Versions >= 10.16 require Java 17. -->
<version>10.15.2.0</version>
<!-- Versions >= 10.16 require Java 17. -->
Comment on lines +33 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<version>10.15.2.0</version>
<!-- Versions >= 10.16 require Java 17. -->
<version>10.15.2.0</version> <!-- Versions >= 10.16 requires Java 17. -->

<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -42,7 +43,7 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<version>1.19.1</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -53,7 +54,7 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>jdbc</artifactId>
<version>1.19.0</version>
<version>1.19.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand All @@ -62,7 +63,7 @@
<plugin>
<groupId>com.exasol</groupId>
<artifactId>project-keeper-maven-plugin</artifactId>
<version>2.9.12</version>
<version>2.9.14</version>
<executions>
<execution>
<goals>
Expand All @@ -76,7 +77,7 @@
<parent>
<artifactId>hamcrest-resultset-matcher-generated-parent</artifactId>
<groupId>com.exasol</groupId>
<version>1.6.1</version>
<version>1.6.2</version>
<relativePath>pk_generated_parent.pom</relativePath>
</parent>
</project>
32 changes: 23 additions & 9 deletions src/main/java/com/exasol/matcher/ResultSetStructureMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,12 @@ private boolean matchRowsInOrder(final ResultSet resultSet) {
boolean ok = true;
try {
int rowIndex = 0;
int matcherRowIndex = 0;
for (final List<Matcher<?>> cellMatcherRow : this.cellMatcherTable) {
++matcherRowIndex;
if (resultSet.next()) {
++rowIndex;
ok = ok && matchValuesInRow(resultSet, rowIndex, cellMatcherRow, true);
ok = ok && matchValuesInRow(resultSet, rowIndex, matcherRowIndex, cellMatcherRow, true);
} else {
ok = false;
}
Expand All @@ -162,12 +164,14 @@ private boolean matchRowsInAnyOrder(final ResultSet resultSet) {
final int numberOfRowMatchers = this.cellMatcherTable.size();
int[] matchesForRowMatcher = new int[numberOfRowMatchers];
int rowIndex = 0;
int matcherRowIndex = 0;
while (resultSet.next()) {
++rowIndex;
boolean anyMatchForThisResultRow = false;
int matcherIndex = 0;
for (final List<Matcher<?>> cellMatcherRow : this.cellMatcherTable) {
if (matchValuesInRow(resultSet, rowIndex, cellMatcherRow, false)) {
++matcherRowIndex;
if (matchValuesInRow(resultSet, rowIndex, matcherRowIndex, cellMatcherRow, false)) {
++matchesForRowMatcher[matcherIndex];
anyMatchForThisResultRow = true;
}
Expand Down Expand Up @@ -241,14 +245,15 @@ private int getExpectedColumnCount() {
* recording it at this early stage is not useful.
* </p>
*
* @param resultSet result set from which to read the cell values
* @param rowIndex index of the row in the result set
* @param cellMatcherRow list of matchers that are tested against the row's cells
* @param resultSet result set from which to read the cell values
* @param rowIndex index of the row in the result set
* @param matcherRowIndex index of the matcher definition
* @param cellMatcherRow list of matchers that are tested against the row's cells
* @param recordFirstDeviation record the first mismatch when set to {@code true}
* @return {@code true} if the given matchers match all cells in this row
*/
private boolean matchValuesInRow(final ResultSet resultSet, final int rowIndex,
final List<Matcher<?>> cellMatcherRow, final boolean recordFirstDeviation) {
private boolean matchValuesInRow(final ResultSet resultSet, final int rowIndex, int matcherRowIndex,
final List<Matcher<?>> cellMatcherRow, final boolean recordFirstDeviation) {
int columnIndex = 0;
try {
for (final Matcher<?> cellMatcher : cellMatcherRow) {
Expand All @@ -262,8 +267,10 @@ private boolean matchValuesInRow(final ResultSet resultSet, final int rowIndex,
}
}
} catch (final SQLException exception) {
throw new AssertionError("Unable to read actual result set value in row " + rowIndex + ", column "
+ columnIndex + ": " + exception.getMessage(), exception);
throw new AssertionError("Row expectation definition " + matcherRowIndex +
" tries to validate the value of row " + rowIndex + ", column "
+ columnIndex + " but that value can't be read from the result set. "
+ "This usually means the column does not exist. \nCaused by SQL error: " + exception.getMessage());
}
return true;
}
Expand Down Expand Up @@ -348,6 +355,13 @@ public static final class Builder {
private Calendar calendar;
private boolean requireSameOrder = true;

/**
* Create a new instance of a {@link ResultSetStructureMatcher.Builder}.
*/
public Builder() {
// intentionally empty
}

/**
* Add a column to the structure to be matched.
*
Expand Down
71 changes: 71 additions & 0 deletions src/test/java/com/exasol/matcher/RowMatcherIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.exasol.matcher;

import org.hamcrest.Matcher;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;

/**
* This integration test runs row matching tests against the Apache Derby database.
*/
class RowMatcherIT extends AbstractResultSetMatcherTest {
@BeforeEach
void beforeEach() throws SQLException {
final Connection connection = DriverManager.getConnection("jdbc:derby:memory:test;create=true");
this.statement = connection.createStatement();
}

@AfterEach
void afterEach() {
execute("DROP TABLE T");
}

// This is a regression test for https://github.com/exasol/hamcrest-resultset-matcher/issues/44
@Test
void testMatchingInAnyOrderAndExpectingThreeColumnsThrowsAssertionErrorWhenResultSetHasOnlyTwoColumns()
throws SQLException {
execute("CREATE TABLE T(I INTEGER, V VARCHAR(20))");
execute("INSERT INTO T VALUES (1, 'a'), (2, 'b')");
final Matcher<ResultSet> anyOrderMatcher = ResultSetStructureMatcher //
.table()//
.row(1, "a", 1) //
.row(greaterThan(0), "b", 3) //
.matchesInAnyOrder();
try(final ResultSet result = query("SELECT * FROM T")) {
final AssertionError error = assertThrows(AssertionError.class,
() -> anyOrderMatcher.matches(result));
assertThat(error.getMessage(), startsWith("Row expectation definition 1 tries to validate the value of row 1, "
+ "column 3 but that value can't be read from the result set. "
+ "This usually means the column does not exist. \nCaused by SQL error:"));
}
}

// This is a regression test for https://github.com/exasol/hamcrest-resultset-matcher/issues/44
@Test
void testMatchingInStrictOrderAndExpectingThreeColumnsThrowsAssertionErrorWhenResultSetHasOnlyTwoColumns()
throws SQLException {
execute("CREATE TABLE T(I INTEGER, V VARCHAR(20))");
execute("INSERT INTO T VALUES (1, 'A'), (2, 'B')");
final Matcher<ResultSet> orderedMatcher = ResultSetStructureMatcher //
.table()//
.row(1, "A", 1) //
.row(greaterThan(0), "B", 3) //
.matches();
try (final ResultSet result = query("SELECT * FROM T")) {
final AssertionError error = assertThrows(AssertionError.class, () -> orderedMatcher.matches(result));
assertThat(error.getMessage(), startsWith("Row expectation definition 1 tries to validate the value of row 1, "
+ "column 3 but that value can't be read from the result set. "
+ "This usually means the column does not exist. \nCaused by SQL error:"));

}
}
}