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

Validating SQL with Hamcrest matchers throws OutOfBoundException #341

Closed
svettwer opened this issue Feb 27, 2018 · 2 comments · Fixed by #977
Closed

Validating SQL with Hamcrest matchers throws OutOfBoundException #341

svettwer opened this issue Feb 27, 2018 · 2 comments · Fixed by #977

Comments

@svettwer
Copy link
Contributor

Hi!

When I try to validate an SQL statement with hamcrest matchers via the citrus hamcrest integration, the parsing of the string expression fails with a StringIndexOutOfBoundsException in the HamcrestValidationMatcher.

I reduced the hamcrest matcher in the example to the essential part. So please don't wonder about the useless allOf()

Example:

@Test
@CitrusTest
public void testAddTodoEntry() {
    variable("todoName", "citrus:concat('todo_', citrus:randomNumber(4))");
    variable("todoDescription", "Description: ${todoName}");

    http()
        .client(todoClient)
        .send()
        .post("/todolist")
        .fork(true)
        .contentType("application/x-www-form-urlencoded")
        .payload("title=${todoName}&description=${todoDescription}");

    receive(jdbcServer)
        .message(JdbcMessage.execute(
                "@assertThat(" +
                        "allOf(" +
                        "startsWith(INSERT INTO todo_entries (id, title, description, done)))@"));

    send(jdbcServer)
        .message(JdbcMessage.result().rowsUpdated(1));

    http()
        .client(todoClient)
        .receive()
        .response(HttpStatus.FOUND);
}

Stacktrace:

14:33:19,408 DEBUG TextMessageValidator| Received message:
JDBCMESSAGE [id: e3775844-848f-4af3-af50-429ede4efb4d, payload: {"execute":{"statement":{"sql":"INSERT INTO todo_entries (id, title, description, done) VALUES (?, ?, ?, ?) - (8839775c-e4c1-4a3d-b8ef-6f499e135ce5,todo_9093,Description: todo_9093,false)"}}}][headers: {citrus_message_id=e3775844-848f-4af3-af50-429ede4efb4d, citrus_message_timestamp=1519738399364, replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@4893b344, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@4893b344, id=25cb86af-c8df-c300-f9f5-d6138b6ca392, timestamp=1519738399395}]
14:33:19,408 DEBUG TextMessageValidator| Control message:
DEFAULTMESSAGE [id: 3a25d593-2c5e-4c5d-9c77-2137586aa130, payload: {"execute":{"statement":{"sql":"@assertThat(allOf(startsWith(INSERT INTO todo_entries (id, title, description, done)))@"}}}][headers: {citrus_message_type=XML, citrus_message_id=3a25d593-2c5e-4c5d-9c77-2137586aa130, citrus_message_timestamp=1519738399404}]
14:33:19,413 INFO  port.LoggingReporter| 
14:33:19,414 ERROR port.LoggingReporter| TEST FAILED TodoListIT.testAddTodoEntry <com.consol.citrus.samples.todolist> Nested exception is: 
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
        at java.lang.String.substring(String.java:1967)
        at com.consol.citrus.validation.matcher.hamcrest.HamcrestValidationMatcher.getMatcher(HamcrestValidationMatcher.java:146)
        at com.consol.citrus.validation.matcher.hamcrest.HamcrestValidationMatcher.validate(HamcrestValidationMatcher.java:83)
        at com.consol.citrus.validation.matcher.ValidationMatcherUtils.resolveValidationMatcher(ValidationMatcherUtils.java:75)
        at com.consol.citrus.validation.json.JsonTextMessageValidator.validateJson(JsonTextMessageValidator.java:194)
        at com.consol.citrus.validation.json.JsonTextMessageValidator.validateJson(JsonTextMessageValidator.java:202)
        at com.consol.citrus.validation.json.JsonTextMessageValidator.validateJson(JsonTextMessageValidator.java:202)
        at com.consol.citrus.validation.json.JsonTextMessageValidator.validateMessage(JsonTextMessageValidator.java:115)
        at com.consol.citrus.validation.json.JsonTextMessageValidator.validateMessage(JsonTextMessageValidator.java:62)
        at com.consol.citrus.validation.AbstractMessageValidator.validateMessage(AbstractMessageValidator.java:46)
        at com.consol.citrus.actions.ReceiveMessageAction.validateMessage(ReceiveMessageAction.java:226)
        at com.consol.citrus.actions.ReceiveMessageAction.doExecute(ReceiveMessageAction.java:127)
        at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:46)
        at com.consol.citrus.dsl.actions.DelegatingTestAction.doExecute(DelegatingTestAction.java:54)
        at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:46)
        at com.consol.citrus.TestCase.executeAction(TestCase.java:234)
        at com.consol.citrus.TestCase.doExecute(TestCase.java:153)
        at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:46)
        at com.consol.citrus.Citrus.run(Citrus.java:393)
        at com.consol.citrus.dsl.testng.TestNGCitrusTest.invokeTestMethod(TestNGCitrusTest.java:132)
        at com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner.invokeTestMethod(TestNGCitrusTestDesigner.java:73)
        at com.consol.citrus.dsl.testng.TestNGCitrusTest.run(TestNGCitrusTest.java:110)
        at com.consol.citrus.dsl.testng.TestNGCitrusTest.run(TestNGCitrusTest.java:56)
        at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:242)
        at org.testng.internal.Invoker.invokeMethod(Invoker.java:567)
        at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:707)
        at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:979)
        at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
        at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
        at org.testng.TestRunner.privateRun(TestRunner.java:648)
        at org.testng.TestRunner.run(TestRunner.java:505)
        at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
        at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
        at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
        at org.testng.SuiteRunner.run(SuiteRunner.java:364)
        at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
        at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
        at org.testng.TestNG.runSuitesSequentially(TestNG.java:1187)
        at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
        at org.testng.TestNG.runSuites(TestNG.java:1028)
        at org.testng.TestNG.run(TestNG.java:996)
        at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:135)
        at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:193)
        at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:94)
        at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:146)
        at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:373)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:334)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:119)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:407)

When I remove the braces, the test is successful

@Test
@CitrusTest
public void testAddTodoEntry() {
    variable("todoName", "citrus:concat('todo_', citrus:randomNumber(4))");
    variable("todoDescription", "Description: ${todoName}");

    http()
        .client(todoClient)
        .send()
        .post("/todolist")
        .fork(true)
        .contentType("application/x-www-form-urlencoded")
        .payload("title=${todoName}&description=${todoDescription}");

    receive(jdbcServer)
        .message(JdbcMessage.execute(
                "@assertThat(" +
                        "allOf(" +
                        "startsWith(INSERT INTO todo_entries))@"));

    send(jdbcServer)
        .message(JdbcMessage.result().rowsUpdated(1));

    http()
        .client(todoClient)
        .receive()
        .response(HttpStatus.FOUND);
}

BR,
Sven

@svettwer svettwer added this to the SOMEDAY milestone Mar 2, 2018
@svettwer svettwer removed this from the SOMEDAY milestone Jul 20, 2018
@svettwer svettwer added good first issue A good point to start from for new contributrs and removed good first issue A good point to start from for new contributrs labels Jan 24, 2019
@christophd christophd removed the READY label Oct 6, 2021
@tschlat
Copy link
Collaborator

tschlat commented Sep 8, 2023

The problem here is the "," in the params array. Internally, the parameter attribute for nested matchers is derived by a simple split(","):

  String[] nestedMatcherParameter = matcherExpression.trim().substring(nestedMatcherName.length() + 1, matcherExpression.trim().length() - 1).split(",");

This probably causes the problem. I have a similar issue and I will investigate on this.

@tschlat
Copy link
Collaborator

tschlat commented Sep 8, 2023

With the provided fix, the above issue can be tackled with:

allOf(startsWith('INSERT INTO todo_entries (id, title, description, done)'))

which the perfectly matches this string:

INSERT INTO todo_entries (id, title, description, done) values (1, 'Invite for meeting', 'Invite the group for a lunch meeting', 'false')

Note that the actual matcher expression needs to be put into '' for proper parsing.

bbortt added a commit that referenced this issue Sep 12, 2023
…_matcher_expressions

fix(#341): Allow comma and brackets in matcher expressions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants