From 9dc5c559064c8f3219236fc9e5d2e3521aa78fab Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Tue, 17 Jan 2023 12:05:10 -0500 Subject: [PATCH] Prettying up FromParam tests Based on a review of how fromParam works --- .../client/test/rows/FromParamTest.java | 474 +++++++++--------- .../client/test/rows/FromParamWriteTest.java | 372 +++++++------- 2 files changed, 430 insertions(+), 416 deletions(-) diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamTest.java index a6659cbc4..3120b3d16 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamTest.java @@ -3,7 +3,11 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.marklogic.client.document.DocumentWriteSet; import com.marklogic.client.expression.PlanBuilder; -import com.marklogic.client.io.*; +import com.marklogic.client.io.BytesHandle; +import com.marklogic.client.io.DocumentMetadataHandle; +import com.marklogic.client.io.Format; +import com.marklogic.client.io.JacksonHandle; +import com.marklogic.client.io.StringHandle; import com.marklogic.client.io.marker.AbstractWriteHandle; import com.marklogic.client.row.RawPlanDefinition; import com.marklogic.client.row.RowRecord; @@ -27,236 +31,240 @@ @ExtendWith(RequiresML11.class) public class FromParamTest extends AbstractOpticUpdateTest { - @Test - public void fromParamWithSimpleJsonArray() { - // Specify the columns that describe the rows that will be passed in - PlanBuilder.Plan plan = op.fromParam("myDocs", "", op.colTypes( - op.colType("lastName", "string"), - op.colType("firstName", "string") - )); - - // Build the rows to bind to the plan - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("lastName", "Smith").put("firstName", "Jane"); - array.addObject().put("lastName", "Jones").put("firstName", "Jack"); - plan = plan.bindParam("myDocs", new JacksonHandle(array), null); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - assertEquals("Jane", rows.get(0).getString("firstName")); - assertEquals("Smith", rows.get(0).getString("lastName")); - assertEquals("Jack", rows.get(1).getString("firstName")); - assertEquals("Jones", rows.get(1).getString("lastName")); - } - - @Test - public void fromParamWithXmlAttachments() { - PlanBuilder.Plan plan = op.fromParam("bindingParam", "", op.colTypes( - op.colType("rowId", "integer"), - op.colType("doc") - )); - - final PlanParamExpr param = op.param("bindingParam"); - - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("rowId", 1).put("doc", "doc1.xml"); - array.addObject().put("rowId", 2).put("doc", "doc2.xml"); - Map attachments = new HashMap<>(); - attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); - attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); - plan = plan.bindParam(param, new JacksonHandle(array), Collections.singletonMap("doc", attachments)); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - RowRecord row = rows.get(0); - assertEquals(1, row.getInt("rowId")); - assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); - - row = rows.get(1); - assertEquals(2, row.getInt("rowId")); - assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); - } - - @Test - public void fromParamWithBinaryAttachments() { - PlanBuilder.Plan plan = op.fromParam("bindingParam", "", op.colTypes( - op.colType("rowId", "integer"), - op.colType("doc") - )); - - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("rowId", 1).put("doc", "doc1.bin"); - array.addObject().put("rowId", 2).put("doc", "doc2.bin"); - Map attachments = new HashMap<>(); - attachments.put("doc1.bin", new BytesHandle("1".getBytes()).withFormat(Format.BINARY)); - attachments.put("doc2.bin", new BytesHandle("2".getBytes()).withFormat(Format.BINARY)); - plan = plan.bindParam("bindingParam", new JacksonHandle(array), Collections.singletonMap("doc", attachments)); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - RowRecord row = rows.get(0); - assertEquals(1, row.getInt("rowId")); - assertEquals("1", row.getContentAs("doc", String.class)); - - row = rows.get(1); - assertEquals(2, row.getInt("rowId")); - assertEquals("2", row.getContentAs("doc", String.class)); - } - - @Test - public void fromParamWithTextAttachments() { - PlanBuilder.Plan plan = op.fromParam("bindingParam", "", op.colTypes( - op.colType("rowId", "integer"), - op.colType("doc") - )); - - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("rowId", 1).put("doc", "doc1.txt"); - array.addObject().put("rowId", 2).put("doc", "doc2.txt"); - Map attachments = new HashMap<>(); - attachments.put("doc1.txt", new StringHandle("doc1-text").withFormat(Format.TEXT)); - attachments.put("doc2.txt", new StringHandle("doc2-text").withFormat(Format.TEXT)); - plan = plan.bindParam("bindingParam", new JacksonHandle(array), Collections.singletonMap("doc", attachments)); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - RowRecord row = rows.get(0); - assertEquals(1, row.getInt("rowId")); - assertEquals("doc1-text", row.getContentAs("doc", String.class)); - - row = rows.get(1); - assertEquals(2, row.getInt("rowId")); - assertEquals("doc2-text", row.getContentAs("doc", String.class)); - } - - /** - * This tests ensures that the bindParam(param, AbstractWriteHandle) methods in RawPlanImpl work correctly. - * Those methods are currently duplicated between RowPlanImpl and PlanSubImpl because the two classes do not have - * a common parent class. So we need at least one test that covers the RawPlanImpl methods, which is this test. - */ - @Test - public void fromParamWithRawPlan() { - // The raw plan is the serialized representation of the plan in fromParamWithTextAttachments - RawPlanDefinition rawPlan = rowManager.newRawPlanDefinition(new StringHandle("{\n" + - " \"$optic\": {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"operators\",\n" + - " \"args\": [\n" + - " {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"from-param\",\n" + - " \"args\": [\n" + - " {\n" + - " \"ns\": \"xs\",\n" + - " \"fn\": \"string\",\n" + - " \"args\": [\n" + - " \"bindingParam\"\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"ns\": \"xs\",\n" + - " \"fn\": \"string\",\n" + - " \"args\": [\n" + - " \"\"\n" + - " ]\n" + - " },\n" + - " [\n" + - " {\n" + - " \"column\": \"rowId\",\n" + - " \"type\": \"integer\",\n" + - " \"nullable\": true\n" + - " },\n" + - " {\n" + - " \"column\": \"doc\",\n" + - " \"type\": \"none\",\n" + - " \"nullable\": true\n" + - " }\n" + - " ]\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "}").withFormat(Format.JSON)); - - final PlanParamExpr param = op.param("bindingParam"); - - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("rowId", 1).put("doc", "doc1.xml"); - array.addObject().put("rowId", 2).put("doc", "doc2.xml"); - Map attachments = new HashMap<>(); - attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); - attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); - PlanBuilder.Plan plan = rawPlan.bindParam(param, new JacksonHandle(array), Collections.singletonMap("doc", attachments)); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - RowRecord row = rows.get(0); - assertEquals(1, row.getInt("rowId")); - assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); - - row = rows.get(1); - assertEquals(2, row.getInt("rowId")); - assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); - } - - /** - * Verifies that a user can have multiple columns that are associated with attachments. - */ - @Test - public void fromParamWithMultipleAttachmentColumns() { - PlanBuilder.Plan plan = op.fromParam("bindingParam", "", op.colTypes( - op.colType("rowId", "integer"), - op.colType("doc"), - op.colType("otherDoc") - )); - - final PlanParamExpr param = op.param("bindingParam"); - - ArrayNode array = mapper.createArrayNode(); - array.addObject().put("rowId", 1).put("doc", "doc1.xml").put("otherDoc", "otherDoc1.xml"); - array.addObject().put("rowId", 2).put("doc", "doc2.xml").put("otherDoc", "otherDoc2.xml"); - Map> columnAttachments = new HashMap<>(); - Map attachments = new HashMap<>(); - attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); - attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); - columnAttachments.put("doc", attachments); - attachments = new HashMap<>(); - attachments.put("otherDoc1.xml", new StringHandle("1").withFormat(Format.XML)); - attachments.put("otherDoc2.xml", new StringHandle("2").withFormat(Format.XML)); - columnAttachments.put("otherDoc", attachments); - plan = plan.bindParam(param, new JacksonHandle(array), columnAttachments); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - RowRecord row = rows.get(0); - assertEquals(1, row.getInt("rowId")); - assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); - assertEquals("1", getRowContentWithoutXmlDeclaration(row, "otherDoc")); - - row = rows.get(1); - assertEquals(2, row.getInt("rowId")); - assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); - assertEquals("2", getRowContentWithoutXmlDeclaration(row, "otherDoc")); - } - - @Test - public void xmlDocumentWriteSetSingleDocBug57894() { - PlanBuilder.Plan plan = op.fromParam("myDocs", "", op.docColTypes()); - - DocumentMetadataHandle metadata = new DocumentMetadataHandle(); - DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); - writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); - plan = plan.bindParam("myDocs", writeSet); - - List rows = resultRows(plan); - assertEquals(1, rows.size()); - RowRecord row = rows.get(0); - assertEquals("/acme/doc1.xml", row.getString("uri")); - assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); - } + @Test + public void fromParamWithSimpleJsonArray() { + // Build the rows to bind to the plan + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("lastName", "Smith").put("firstName", "Jane"); + array.addObject().put("lastName", "Jones").put("firstName", "Jack"); + + // Specify the columns that describe the rows that will be passed in + PlanBuilder.Plan plan = op + .fromParam("myDocs", "", op.colTypes( + op.colType("lastName", "string"), + op.colType("firstName", "string") + )) + .bindParam("myDocs", new JacksonHandle(array)); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + assertEquals("Jane", rows.get(0).getString("firstName")); + assertEquals("Smith", rows.get(0).getString("lastName")); + assertEquals("Jack", rows.get(1).getString("firstName")); + assertEquals("Jones", rows.get(1).getString("lastName")); + } + + @Test + public void fromParamWithXmlAttachments() { + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("rowId", 1).put("doc", "doc1.xml"); + array.addObject().put("rowId", 2).put("doc", "doc2.xml"); + Map attachments = new HashMap<>(); + attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); + attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); + + PlanBuilder.Plan plan = op + .fromParam("myXmlData", "", op.colTypes( + op.colType("rowId", "integer"), + op.colType("doc") + )) + .bindParam(op.param("myXmlData"), new JacksonHandle(array), Collections.singletonMap("doc", attachments)); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + RowRecord row = rows.get(0); + assertEquals(1, row.getInt("rowId")); + assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); + + row = rows.get(1); + assertEquals(2, row.getInt("rowId")); + assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); + } + + @Test + public void fromParamWithBinaryAttachments() { + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("rowId", 1).put("doc", "doc1.bin"); + array.addObject().put("rowId", 2).put("doc", "doc2.bin"); + Map attachments = new HashMap<>(); + attachments.put("doc1.bin", new BytesHandle("1".getBytes()).withFormat(Format.BINARY)); + attachments.put("doc2.bin", new BytesHandle("2".getBytes()).withFormat(Format.BINARY)); + + PlanBuilder.Plan plan = op + .fromParam("myBinaryData", "", op.colTypes( + op.colType("rowId", "integer"), + op.colType("doc") + )) + .bindParam("myBinaryData", new JacksonHandle(array), Collections.singletonMap("doc", attachments)); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + RowRecord row = rows.get(0); + assertEquals(1, row.getInt("rowId")); + assertEquals("1", row.getContentAs("doc", String.class)); + + row = rows.get(1); + assertEquals(2, row.getInt("rowId")); + assertEquals("2", row.getContentAs("doc", String.class)); + } + + @Test + public void fromParamWithTextAttachments() { + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("rowId", 1).put("doc", "doc1.txt"); + array.addObject().put("rowId", 2).put("doc", "doc2.txt"); + Map attachments = new HashMap<>(); + attachments.put("doc1.txt", new StringHandle("doc1-text").withFormat(Format.TEXT)); + attachments.put("doc2.txt", new StringHandle("doc2-text").withFormat(Format.TEXT)); + + PlanBuilder.Plan plan = op + .fromParam("myTextData", "", op.colTypes( + op.colType("rowId", "integer"), + op.colType("doc") + )) + .bindParam("myTextData", new JacksonHandle(array), Collections.singletonMap("doc", attachments)); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + RowRecord row = rows.get(0); + assertEquals(1, row.getInt("rowId")); + assertEquals("doc1-text", row.getContentAs("doc", String.class)); + + row = rows.get(1); + assertEquals(2, row.getInt("rowId")); + assertEquals("doc2-text", row.getContentAs("doc", String.class)); + } + + /** + * This tests ensures that the bindParam(param, AbstractWriteHandle) methods in RawPlanImpl work correctly. + * Those methods are currently duplicated between RowPlanImpl and PlanSubImpl because the two classes do not have + * a common parent class. So we need at least one test that covers the RawPlanImpl methods, which is this test. + */ + @Test + public void fromParamWithRawPlan() { + // The raw plan is the serialized representation of the plan in fromParamWithTextAttachments + RawPlanDefinition rawPlan = rowManager.newRawPlanDefinition(new StringHandle("{\n" + + " \"$optic\": {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"operators\",\n" + + " \"args\": [\n" + + " {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"from-param\",\n" + + " \"args\": [\n" + + " {\n" + + " \"ns\": \"xs\",\n" + + " \"fn\": \"string\",\n" + + " \"args\": [\n" + + " \"bindingParam\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"ns\": \"xs\",\n" + + " \"fn\": \"string\",\n" + + " \"args\": [\n" + + " \"\"\n" + + " ]\n" + + " },\n" + + " [\n" + + " {\n" + + " \"column\": \"rowId\",\n" + + " \"type\": \"integer\",\n" + + " \"nullable\": true\n" + + " },\n" + + " {\n" + + " \"column\": \"doc\",\n" + + " \"type\": \"none\",\n" + + " \"nullable\": true\n" + + " }\n" + + " ]\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}").withFormat(Format.JSON)); + + final PlanParamExpr param = op.param("bindingParam"); + + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("rowId", 1).put("doc", "doc1.xml"); + array.addObject().put("rowId", 2).put("doc", "doc2.xml"); + Map attachments = new HashMap<>(); + attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); + attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); + PlanBuilder.Plan plan = rawPlan.bindParam(param, new JacksonHandle(array), Collections.singletonMap("doc", attachments)); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + RowRecord row = rows.get(0); + assertEquals(1, row.getInt("rowId")); + assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); + + row = rows.get(1); + assertEquals(2, row.getInt("rowId")); + assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); + } + + /** + * Verifies that a user can have multiple columns that are associated with attachments. + */ + @Test + public void fromParamWithMultipleAttachmentColumns() { + ArrayNode array = mapper.createArrayNode(); + array.addObject().put("rowId", 1).put("doc", "doc1.xml").put("otherDoc", "otherDoc1.xml"); + array.addObject().put("rowId", 2).put("doc", "doc2.xml").put("otherDoc", "otherDoc2.xml"); + Map> columnAttachments = new HashMap<>(); + Map attachments = new HashMap<>(); + attachments.put("doc1.xml", new StringHandle("1").withFormat(Format.XML)); + attachments.put("doc2.xml", new StringHandle("2").withFormat(Format.XML)); + columnAttachments.put("doc", attachments); + attachments = new HashMap<>(); + attachments.put("otherDoc1.xml", new StringHandle("1").withFormat(Format.XML)); + attachments.put("otherDoc2.xml", new StringHandle("2").withFormat(Format.XML)); + columnAttachments.put("otherDoc", attachments); + + + PlanBuilder.Plan plan = op + .fromParam("bindingParam", "", op.colTypes( + op.colType("rowId", "integer"), + op.colType("doc"), + op.colType("otherDoc") + )) + .bindParam(op.param("bindingParam"), new JacksonHandle(array), columnAttachments); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + RowRecord row = rows.get(0); + assertEquals(1, row.getInt("rowId")); + assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); + assertEquals("1", getRowContentWithoutXmlDeclaration(row, "otherDoc")); + + row = rows.get(1); + assertEquals(2, row.getInt("rowId")); + assertEquals("2", getRowContentWithoutXmlDeclaration(row, "doc")); + assertEquals("2", getRowContentWithoutXmlDeclaration(row, "otherDoc")); + } + + @Test + public void xmlDocumentWriteSetSingleDocBug57894() { + + DocumentMetadataHandle metadata = new DocumentMetadataHandle(); + DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); + writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); + + PlanBuilder.Plan plan = op + .fromParam("myDocs", "", op.docColTypes()) + .bindParam("myDocs", writeSet); + + List rows = resultRows(plan); + assertEquals(1, rows.size()); + RowRecord row = rows.get(0); + assertEquals("/acme/doc1.xml", row.getString("uri")); + assertEquals("1", getRowContentWithoutXmlDeclaration(row, "doc")); + } } diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamWriteTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamWriteTest.java index 760d99826..4514906b2 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamWriteTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/FromParamWriteTest.java @@ -20,194 +20,200 @@ import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(RequiresML11.class) public class FromParamWriteTest extends AbstractOpticUpdateTest { - @Test - public void jsonDocumentWithAllMetadata() { - PlanBuilder.Plan plan = op - .fromParam("myDocs", "", op.docColTypes()) - .write(); - - DocumentMetadataHandle metadata = newDefaultMetadata() - .withQuality(2) - .withMetadataValue("meta1", "value1") - .withMetadataValue("meta2", "value2") - .withCollections("common-coll", "other-coll-1"); - - DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); - writeSet.add("/acme/doc1.json", metadata, - new JacksonHandle(new ObjectMapper().createObjectNode().put("value", 1))); - plan = plan.bindParam("myDocs", writeSet); - - List rows = resultRows(plan); - assertEquals(1, rows.size()); - - verifyMetadata("/acme/doc1.json", docMetadata -> { - assertEquals(2, docMetadata.getQuality()); - assertEquals(DocumentMetadataHandle.Capability.READ, docMetadata.getPermissions().get("rest-reader").iterator().next()); - assertEquals(DocumentMetadataHandle.Capability.UPDATE, docMetadata.getPermissions().get("test-rest-writer").iterator().next()); - assertTrue(docMetadata.getCollections().contains("common-coll")); - assertTrue(docMetadata.getCollections().contains("other-coll-1")); - assertEquals("value1", docMetadata.getMetadataValues().get("meta1")); - assertEquals("value2", docMetadata.getMetadataValues().get("meta2")); - }); - } - - @Test - public void xmlDocumentWriteSet() { - PlanBuilder.Plan plan = op.fromParam("myDocs", "", op.docColTypes()).write(); - - DocumentMetadataHandle metadata = newDefaultMetadata(); - DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); - writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); - writeSet.add("/acme/doc2.xml", metadata, new StringHandle("2").withFormat(Format.XML)); - plan = plan.bindParam("myDocs", writeSet); - - List rows = resultRows(plan); - assertEquals(2, rows.size()); - - verifyXmlDocContent("/acme/doc1.xml", "1"); - verifyXmlDocContent("/acme/doc2.xml", "2"); - } - - /** - * This test is used to document the fact that mixed content cannot be written yet. - */ - @Test - public void jsonAndXmlDocumentsInDocumentWriteSet() { - PlanBuilder.Plan plan = op.fromParam("myDocs", "", op.docColTypes()).write(); - - DocumentMetadataHandle metadata = newDefaultMetadata(); - DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); - writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1")); - writeSet.add("/acme/doc2.json", metadata, new StringHandle("{\"doc\":2}").withFormat(Format.JSON)); - PlanBuilder.Plan finalPlan = plan.bindParam("myDocs", writeSet); - - // The reason why this fails has changed a few times, so this test no longer asserts on the message, but - // rather just that an exception occurs - Exception ex = assertThrows(Exception.class, () -> rowManager.execute(finalPlan)); - System.out.println(ex.getMessage()); - } - - /** - * Verifies that the path through RawPlanImpl supports binding content params. - */ - @Test - public void rawPlanAndDocumentWriteSet() { - RawPlanDefinition rawPlan = rowManager.newRawPlanDefinition(new StringHandle("{\n" + - " \"$optic\": {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"operators\",\n" + - " \"args\": [\n" + - " {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"from-param\",\n" + - " \"args\": [\n" + - " {\n" + - " \"ns\": \"xs\",\n" + - " \"fn\": \"string\",\n" + - " \"args\": [\n" + - " \"myDocs\"\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"ns\": \"xs\",\n" + - " \"fn\": \"string\",\n" + - " \"args\": [\n" + - " \"\"\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"doc-col-types\",\n" + - " \"args\": []\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"ns\": \"op\",\n" + - " \"fn\": \"write\",\n" + - " \"args\": []\n" + - " }\n" + - " ]\n" + - " }\n" + - "}")); - - DocumentMetadataHandle metadata = newDefaultMetadata(); - DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); - writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); - writeSet.add("/acme/doc2.xml", metadata, new StringHandle("2").withFormat(Format.XML)); - PlanBuilder.Plan plan = rawPlan.bindParam("myDocs", writeSet); - - rowManager.execute(plan); - - verifyXmlDocContent("/acme/doc1.xml", "1"); - verifyXmlDocContent("/acme/doc2.xml", "2"); - } - - @Test - public void temporalWrite() { - final String uri = "/acme/doc1-temporal.json"; - final String temporalCollection = "temporal-collection"; - ObjectNode temporalContent = newTemporalContent(); - - rowManager.execute(op - .fromDocDescriptors(op.docDescriptor( - new DocumentWriteOperationImpl(uri, newDefaultMetadata(), new JacksonHandle(temporalContent), temporalCollection) - )) - .write(op.docCols(null, op.xs.stringSeq("uri", "doc", "temporalCollection", "permissions")))); - - verifyJsonDoc(uri, doc -> { - String systemEnd = doc.get("system-end").asText(); - assertTrue(systemEnd.startsWith("9999"), "system-end should have been populated by ML: " + doc); - assertTrue( - StringUtils.isNotEmpty(doc.get("system-start").asText()), + @Test + public void jsonDocumentWithAllMetadata() { + DocumentMetadataHandle metadata = newDefaultMetadata() + .withQuality(2) + .withMetadataValue("meta1", "value1") + .withMetadataValue("meta2", "value2") + .withCollections("common-coll", "other-coll-1"); + + DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); + writeSet.add("/acme/doc1.json", metadata, + new JacksonHandle(new ObjectMapper().createObjectNode().put("value", 1))); + + PlanBuilder.Plan plan = op + .fromParam("myDocs", "", op.docColTypes()) + .write() + .bindParam("myDocs", writeSet); + + List rows = resultRows(plan); + assertEquals(1, rows.size()); + + verifyMetadata("/acme/doc1.json", docMetadata -> { + assertEquals(2, docMetadata.getQuality()); + assertEquals(DocumentMetadataHandle.Capability.READ, docMetadata.getPermissions().get("rest-reader").iterator().next()); + assertEquals(DocumentMetadataHandle.Capability.UPDATE, docMetadata.getPermissions().get("test-rest-writer").iterator().next()); + assertTrue(docMetadata.getCollections().contains("common-coll")); + assertTrue(docMetadata.getCollections().contains("other-coll-1")); + assertEquals("value1", docMetadata.getMetadataValues().get("meta1")); + assertEquals("value2", docMetadata.getMetadataValues().get("meta2")); + }); + } + + @Test + public void xmlDocumentWriteSet() { + DocumentMetadataHandle metadata = newDefaultMetadata(); + DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); + writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); + writeSet.add("/acme/doc2.xml", metadata, new StringHandle("2").withFormat(Format.XML)); + + PlanBuilder.Plan plan = op + .fromParam("myDocs", "", op.docColTypes()) + .write() + .bindParam("myDocs", writeSet); + + List rows = resultRows(plan); + assertEquals(2, rows.size()); + + verifyXmlDocContent("/acme/doc1.xml", "1"); + verifyXmlDocContent("/acme/doc2.xml", "2"); + } + + /** + * This test is used to document the fact that mixed content cannot be written yet. + */ + @Test + public void jsonAndXmlDocumentsInDocumentWriteSet() { + DocumentMetadataHandle metadata = newDefaultMetadata(); + DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); + writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1")); + writeSet.add("/acme/doc2.json", metadata, new StringHandle("{\"doc\":2}").withFormat(Format.JSON)); + + PlanBuilder.Plan plan = op + .fromParam("myDocs", "", op.docColTypes()) + .write() + .bindParam("myDocs", writeSet); + + // The reason why this fails has changed a few times, so this test no longer asserts on the message, but + // rather just that an exception occurs + Exception ex = assertThrows(Exception.class, () -> rowManager.execute(plan)); + System.out.println(ex.getMessage()); + } + + /** + * Verifies that the path through RawPlanImpl supports binding content params. + */ + @Test + public void rawPlanAndDocumentWriteSet() { + RawPlanDefinition rawPlan = rowManager.newRawPlanDefinition(new StringHandle("{\n" + + " \"$optic\": {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"operators\",\n" + + " \"args\": [\n" + + " {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"from-param\",\n" + + " \"args\": [\n" + + " {\n" + + " \"ns\": \"xs\",\n" + + " \"fn\": \"string\",\n" + + " \"args\": [\n" + + " \"myDocs\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"ns\": \"xs\",\n" + + " \"fn\": \"string\",\n" + + " \"args\": [\n" + + " \"\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"doc-col-types\",\n" + + " \"args\": []\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"ns\": \"op\",\n" + + " \"fn\": \"write\",\n" + + " \"args\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}")); + + DocumentMetadataHandle metadata = newDefaultMetadata(); + DocumentWriteSet writeSet = Common.client.newDocumentManager().newWriteSet(); + writeSet.add("/acme/doc1.xml", metadata, new StringHandle("1").withFormat(Format.XML)); + writeSet.add("/acme/doc2.xml", metadata, new StringHandle("2").withFormat(Format.XML)); + PlanBuilder.Plan plan = rawPlan.bindParam("myDocs", writeSet); + + rowManager.execute(plan); + + verifyXmlDocContent("/acme/doc1.xml", "1"); + verifyXmlDocContent("/acme/doc2.xml", "2"); + } + + @Test + public void temporalWrite() { + final String uri = "/acme/doc1-temporal.json"; + final String temporalCollection = "temporal-collection"; + ObjectNode temporalContent = newTemporalContent(); + + rowManager.execute(op + .fromDocDescriptors(op.docDescriptor( + new DocumentWriteOperationImpl(uri, newDefaultMetadata(), new JacksonHandle(temporalContent), temporalCollection) + )) + .write(op.docCols(null, op.xs.stringSeq("uri", "doc", "temporalCollection", "permissions")))); + + verifyJsonDoc(uri, doc -> { + String systemEnd = doc.get("system-end").asText(); + assertTrue(systemEnd.startsWith("9999"), "system-end should have been populated by ML: " + doc); + assertTrue( + StringUtils.isNotEmpty(doc.get("system-start").asText()), "system-start should have been populated by ML: " + doc); - }); + }); - verifyMetadata(uri, metadata -> { - assertTrue(metadata.getCollections().contains("latest"), + verifyMetadata(uri, metadata -> { + assertTrue(metadata.getCollections().contains("latest"), "The document should be in the 'latest' collection if it was correctly inserted via temporal.documentInsert"); - assertTrue(metadata.getCollections().contains(temporalCollection)); - }); - - // Update the doc to ensure that a new version is created - temporalContent.put("hello", "world"); - rowManager.execute(op - .fromDocDescriptors(op.docDescriptor( - new DocumentWriteOperationImpl(uri, null, new JacksonHandle(temporalContent), temporalCollection) - )) - .write(op.docCols(null, op.xs.stringSeq("uri", "doc", "temporalCollection")))); - - // Verify doc and that we now have 2 versions - verifyJsonDoc(uri, doc -> assertEquals("world", doc.get("hello").asText())); - List rows = resultRows(op.fromDocUris(op.cts.collectionQuery(temporalCollection))); - assertEquals(2, rows.size(), "Should have 2 versions in the temporal collection"); - } - - @Test - public void invalidTemporalWrite() { - final String temporalCollection = "temporal-collection"; - assertThrows( - // The error message is not of interest, as it's controlled by temporal.documentInsert and not by Optic. - // Just need to verify that this fails as expected. - FailedRequestException.class, - () -> rowManager.execute(op - .fromDocDescriptors(op.docDescriptor( - new DocumentWriteOperationImpl("/acme/this-should-fail.json", newDefaultMetadata(), - new JacksonHandle(mapper.createObjectNode().put("this is", "missing temporal fields")), temporalCollection - ) - )) - .write()) - ); - } - - private void verifyXmlDocContent(String uri, String expectedContent) { - verifyXmlDoc(uri, content -> { - assertTrue(content.contains(expectedContent), "Unexpected content: " + content); - }); - } + assertTrue(metadata.getCollections().contains(temporalCollection)); + }); + + // Update the doc to ensure that a new version is created + temporalContent.put("hello", "world"); + rowManager.execute(op + .fromDocDescriptors(op.docDescriptor( + new DocumentWriteOperationImpl(uri, null, new JacksonHandle(temporalContent), temporalCollection) + )) + .write(op.docCols(null, op.xs.stringSeq("uri", "doc", "temporalCollection")))); + + // Verify doc and that we now have 2 versions + verifyJsonDoc(uri, doc -> assertEquals("world", doc.get("hello").asText())); + List rows = resultRows(op.fromDocUris(op.cts.collectionQuery(temporalCollection))); + assertEquals(2, rows.size(), "Should have 2 versions in the temporal collection"); + } + + @Test + public void invalidTemporalWrite() { + final String temporalCollection = "temporal-collection"; + assertThrows( + // The error message is not of interest, as it's controlled by temporal.documentInsert and not by Optic. + // Just need to verify that this fails as expected. + FailedRequestException.class, + () -> rowManager.execute(op + .fromDocDescriptors(op.docDescriptor( + new DocumentWriteOperationImpl("/acme/this-should-fail.json", newDefaultMetadata(), + new JacksonHandle(mapper.createObjectNode().put("this is", "missing temporal fields")), temporalCollection + ) + )) + .write()) + ); + } + + private void verifyXmlDocContent(String uri, String expectedContent) { + verifyXmlDoc(uri, content -> { + assertTrue(content.contains(expectedContent), "Unexpected content: " + content); + }); + } }