|
15 | 15 | */ |
16 | 16 | package com.marklogic.client.test.rows; |
17 | 17 |
|
18 | | -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; |
19 | | -import static org.junit.Assert.assertArrayEquals; |
20 | | -import static org.junit.Assert.assertEquals; |
21 | | -import static org.junit.Assert.assertFalse; |
22 | | -import static org.junit.Assert.assertNotNull; |
23 | | -import static org.junit.Assert.assertTrue; |
24 | | - |
25 | | -import java.io.IOException; |
26 | | -import java.io.LineNumberReader; |
27 | | -import java.io.StringWriter; |
28 | | -import java.util.*; |
29 | | -import java.util.stream.Collectors; |
30 | | - |
31 | | -import javax.xml.namespace.QName; |
32 | | -import javax.xml.transform.OutputKeys; |
33 | | -import javax.xml.transform.Transformer; |
34 | | -import javax.xml.transform.TransformerConfigurationException; |
35 | | -import javax.xml.transform.TransformerException; |
36 | | -import javax.xml.transform.TransformerFactory; |
37 | | -import javax.xml.transform.TransformerFactoryConfigurationError; |
38 | | -import javax.xml.transform.dom.DOMSource; |
39 | | -import javax.xml.transform.stream.StreamResult; |
40 | | -import javax.xml.xpath.XPathExpressionException; |
41 | | - |
42 | | -import com.fasterxml.jackson.databind.node.ObjectNode; |
| 18 | +import com.fasterxml.jackson.databind.JsonNode; |
| 19 | +import com.fasterxml.jackson.databind.ObjectMapper; |
| 20 | +import com.marklogic.client.FailedRequestException; |
43 | 21 | import com.marklogic.client.datamovement.DataMovementManager; |
44 | 22 | import com.marklogic.client.datamovement.WriteBatcher; |
| 23 | +import com.marklogic.client.document.DocumentManager; |
| 24 | +import com.marklogic.client.expression.PlanBuilder; |
45 | 25 | import com.marklogic.client.io.*; |
46 | 26 | import com.marklogic.client.query.DeleteQueryDefinition; |
47 | 27 | import com.marklogic.client.query.QueryManager; |
48 | 28 | import com.marklogic.client.row.*; |
| 29 | +import com.marklogic.client.row.RowManager.RowSetPart; |
| 30 | +import com.marklogic.client.row.RowManager.RowStructure; |
| 31 | +import com.marklogic.client.row.RowRecord.ColumnKind; |
| 32 | +import com.marklogic.client.test.Common; |
49 | 33 | import com.marklogic.client.type.*; |
| 34 | +import com.marklogic.client.util.EditableNamespaceContext; |
50 | 35 | import org.junit.AfterClass; |
51 | 36 | import org.junit.BeforeClass; |
52 | 37 | import org.junit.Ignore; |
|
57 | 42 | import org.w3c.dom.NodeList; |
58 | 43 | import org.xml.sax.SAXException; |
59 | 44 |
|
60 | | -import com.fasterxml.jackson.databind.JsonNode; |
61 | | -import com.fasterxml.jackson.databind.ObjectMapper; |
62 | | -import com.marklogic.client.document.DocumentManager; |
63 | | -import com.marklogic.client.expression.PlanBuilder; |
64 | | -import com.marklogic.client.row.RowManager.RowSetPart; |
65 | | -import com.marklogic.client.row.RowManager.RowStructure; |
66 | | -import com.marklogic.client.row.RowRecord.ColumnKind; |
67 | | -import com.marklogic.client.test.Common; |
68 | | -import com.marklogic.client.util.EditableNamespaceContext; |
| 45 | +import javax.xml.namespace.QName; |
| 46 | +import javax.xml.transform.*; |
| 47 | +import javax.xml.transform.dom.DOMSource; |
| 48 | +import javax.xml.transform.stream.StreamResult; |
| 49 | +import javax.xml.xpath.XPathExpressionException; |
| 50 | +import java.io.IOException; |
| 51 | +import java.io.LineNumberReader; |
| 52 | +import java.io.StringWriter; |
| 53 | +import java.util.*; |
| 54 | +import java.util.stream.Collectors; |
| 55 | + |
| 56 | +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; |
| 57 | +import static org.junit.Assert.*; |
69 | 58 |
|
70 | 59 | public class RowManagerTest { |
71 | 60 | private static String[] uris = null; |
@@ -528,26 +517,42 @@ public void testSQL0Result() { |
528 | 517 | } |
529 | 518 |
|
530 | 519 | @Test |
531 | | - public void testSQLException() { |
| 520 | + public void testErrorWhileStreamingRows() { |
532 | 521 | if (!Common.markLogicIsVersion11OrHigher()) { |
533 | 522 | return; |
534 | 523 | } |
535 | 524 |
|
536 | | - RowManager rowMgr = Common.client.newRowManager(); |
537 | | - PlanBuilder p = rowMgr.newPlanBuilder(); |
538 | | - PlanBuilder.ExportablePlan builtPlan = |
539 | | - p.fromSql("select case when lastName = 'Davis' then fn_error(fn_qname('', 'SQL-TABLENOTFOUND'), 'Internal Server Error') end, opticUnitTest.musician.* from (select * from opticUnitTest.musician order by lastName)"); |
540 | | - String exception = ""; |
541 | | - int rowNum = 0; |
542 | | - try { |
543 | | - for (RowRecord row: rowMgr.resultRows(builtPlan)) { |
544 | | - rowNum++; |
545 | | - } |
546 | | - } catch (Exception e) { |
547 | | - exception = e.toString(); |
548 | | - } |
549 | | - assertEquals(0, rowNum); |
550 | | - assertEquals("com.marklogic.client.FailedRequestException: failed to apply resource at rows: SQL-TABLENOTFOUND, Internal Server Error", exception); |
| 525 | + final String validQueryThatEventuallyThrowsAnError = "select case " + |
| 526 | + "when lastName = 'Davis' then fn_error(fn_qname('', 'SQL-TABLENOTFOUND'), 'Internal Server Error') end, " + |
| 527 | + "opticUnitTest.musician.* from (select * from opticUnitTest.musician order by lastName)"; |
| 528 | + |
| 529 | + RowManager rowManager = Common.client.newRowManager(); |
| 530 | + PlanBuilder.ModifyPlan plan = rowManager.newPlanBuilder().fromSql(validQueryThatEventuallyThrowsAnError); |
| 531 | + |
| 532 | + FailedRequestException ex = assertThrows( |
| 533 | + "The SQL query is designed to not immediately fail - it will immediately return a 200 status code to the " + |
| 534 | + "Java Client because the query itself can be executed - but will fail later as it streams rows; " + |
| 535 | + "specifically, it will fail on the fourth row, which is the 'Davis' row. " + |
| 536 | + "If chunking is configured correctly for the /v1/rows requests - i.e. if the " + |
| 537 | + "'TE' header is present - then ML should return trailers in the HTTP response named 'ml-error-code' and " + |
| 538 | + "'ml-error-message'. Those are intended to indicate that while a 200 was returned, an error occurred later " + |
| 539 | + "while streaming data back. The Java Client is then expected to detect those trailers and throw a " + |
| 540 | + "FailedRequestException. If the Java Client does not do that, then no exception will be thrown and this " + |
| 541 | + "assertion will fail.", FailedRequestException.class, () -> rowManager.resultRows(plan)); |
| 542 | + |
| 543 | + assertEquals("A 500 is expected, even though ML immediately returned a 200 before it started streaming any data " + |
| 544 | + "back; a 500 is used instead of a 400 here as we don't have a reliable way of knowing if the error " + |
| 545 | + "occurred due to a bad request by the user, since the query was valid in the sense that it could be " + |
| 546 | + "executed", 500, ex.getServerStatusCode()); |
| 547 | + |
| 548 | + assertEquals("The server error message is expected to be the value of the 'ml-error-message' trailer", |
| 549 | + "SQL-TABLENOTFOUND", ex.getServerMessage()); |
| 550 | + |
| 551 | + assertEquals( |
| 552 | + "The exception message is expected to be a formatted message containing the values of the 'ml-error-code' and " + |
| 553 | + "'ml-error-message' trailers", |
| 554 | + "Local message: failed to apply resource at rows: SQL-TABLENOTFOUND, Internal Server Error. Server Message: SQL-TABLENOTFOUND", |
| 555 | + ex.getMessage()); |
551 | 556 | } |
552 | 557 |
|
553 | 558 | @Test |
|
0 commit comments