diff --git a/extended-it/build.gradle b/extended-it/build.gradle index 7d3c218daf..51d225dc99 100644 --- a/extended-it/build.gradle +++ b/extended-it/build.gradle @@ -57,6 +57,7 @@ dependencies { testImplementation group: 'org.testcontainers', name: 'milvus', version: '1.20.2' testImplementation group: 'org.apache.poi', name: 'poi', version: '5.1.0' testImplementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.1.0' + testImplementation 'com.azure:azure-storage-blob:12.22.0' configurations.all { exclude group: 'org.slf4j', module: 'slf4j-nop' exclude group: 'ch.qos.logback', module: 'logback-classic' diff --git a/extended-it/src/test/java/apoc/azure/AzureStorageBaseTest.java b/extended-it/src/test/java/apoc/azure/AzureStorageBaseTest.java new file mode 100644 index 0000000000..a1c099be86 --- /dev/null +++ b/extended-it/src/test/java/apoc/azure/AzureStorageBaseTest.java @@ -0,0 +1,76 @@ +package apoc.azure; + +import com.azure.core.util.Context; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobContainerClientBuilder; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; +import org.apache.commons.io.FileUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.UUID; + +public class AzureStorageBaseTest { + + public static GenericContainer azuriteContainer; + public static BlobContainerClient containerClient; + + @BeforeClass + public static void setUp() throws Exception { + DockerImageName azuriteImg = DockerImageName.parse("mcr.microsoft.com/azure-storage/azurite"); + azuriteContainer = new GenericContainer<>(azuriteImg) + .withExposedPorts(10000); + + azuriteContainer.start(); + + var accountName = "devstoreaccount1"; + var accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; + var blobEndpoint = "http://%s:%d/%s".formatted(azuriteContainer.getHost(), azuriteContainer.getMappedPort(10000), accountName); + var connectionString = "DefaultEndpointsProtocol=http;AccountName=%s;AccountKey=%s;BlobEndpoint=%s;" + .formatted(accountName, accountKey, blobEndpoint); + + // Create container + String containerName = "test-container"; + containerClient = new BlobContainerClientBuilder() + .connectionString(connectionString) + .containerName(containerName) + .buildClient(); + containerClient.create(); + } + + @AfterClass + public static void teardown() { + azuriteContainer.close(); + } + + public static String putToAzureStorageAndGetUrl(String url) { + try { + File file = new File(url); + byte[] content = FileUtils.readFileToByteArray(file); + + var blobClient = getBlobClient(content); + BlobSasPermission permission = new BlobSasPermission().setReadPermission(true).setExecutePermission(true).setAddPermission(true); + OffsetDateTime expiryTime = OffsetDateTime.now().plusHours(1); + String sasToken = blobClient.generateSas(new BlobServiceSasSignatureValues(expiryTime, permission), new Context("Azure-Storage-Log-String-To-Sign", "true")); + return blobClient.getBlobUrl() + "?" + sasToken; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static BlobClient getBlobClient(byte[] content) { + var blobName = "blob-" + UUID.randomUUID(); + var blobClient = containerClient.getBlobClient(blobName); + blobClient.upload(new ByteArrayInputStream(content)); + return blobClient; + } + +} diff --git a/extended-it/src/test/java/apoc/azure/ImportAzureStorageTest.java b/extended-it/src/test/java/apoc/azure/ImportAzureStorageTest.java new file mode 100644 index 0000000000..3dfa698ea8 --- /dev/null +++ b/extended-it/src/test/java/apoc/azure/ImportAzureStorageTest.java @@ -0,0 +1,67 @@ +package apoc.azure; + +import apoc.export.arrow.ExportArrow; +import apoc.export.arrow.ImportArrow; +import apoc.export.arrow.ImportArrowTestUtil; +import apoc.load.Gexf; +import apoc.meta.Meta; +import apoc.util.TestUtil; +import apoc.util.s3.S3BaseTest; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import org.neo4j.test.rule.DbmsRule; +import org.neo4j.test.rule.ImpermanentDbmsRule; + +import java.io.File; +import java.util.Map; + +import static apoc.ApocConfig.APOC_EXPORT_FILE_ENABLED; +import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED; +import static apoc.ApocConfig.apocConfig; +import static apoc.export.arrow.ImportArrowTestUtil.ARROW_BASE_FOLDER; +import static apoc.export.arrow.ImportArrowTestUtil.MAPPING_ALL; +import static apoc.export.arrow.ImportArrowTestUtil.prepareDbForArrow; +import static apoc.export.arrow.ImportArrowTestUtil.testImportCommon; +import static apoc.util.ExtendedITUtil.EXTENDED_PATH; +import static apoc.util.ExtendedTestUtil.clearDb; +import static apoc.util.GexfTestUtil.testImportGexfCommon; + +public class ImportAzureStorageTest extends AzureStorageBaseTest { + private static File directory = new File(ARROW_BASE_FOLDER); + static { //noinspection ResultOfMethodCallIgnored + directory.mkdirs(); + } + + @ClassRule + public static DbmsRule db = new ImpermanentDbmsRule(); + + @BeforeClass + public static void beforeClass() { + TestUtil.registerProcedure(db, ExportArrow.class, ImportArrow.class, Meta.class, Gexf.class); + prepareDbForArrow(db); + apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); + apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true); + } + + @Test + public void testImportArrow() { + String file = db.executeTransactionally("CALL apoc.export.arrow.all('test_all.arrow') YIELD file", + Map.of(), + ImportArrowTestUtil::extractFileName); + + String fileWithPath = ARROW_BASE_FOLDER + File.separator + file; + String url = putToAzureStorageAndGetUrl(fileWithPath); + + testImportCommon(db, url, MAPPING_ALL); + } + + @Test + public void testImportGexf() { + clearDb(db); + String filename = EXTENDED_PATH + "src/test/resources/gexf/data.gexf"; + String url = putToAzureStorageAndGetUrl(filename); + testImportGexfCommon(db, url); + } +} diff --git a/extended-it/src/test/java/apoc/azure/LoadAzureStorageTest.java b/extended-it/src/test/java/apoc/azure/LoadAzureStorageTest.java new file mode 100644 index 0000000000..25814747d4 --- /dev/null +++ b/extended-it/src/test/java/apoc/azure/LoadAzureStorageTest.java @@ -0,0 +1,87 @@ +package apoc.azure; + +import apoc.load.LoadCsv; +import apoc.load.LoadDirectory; +import apoc.load.LoadHtml; +import apoc.load.LoadJson; +import apoc.load.Xml; +import apoc.load.xls.LoadXls; +import apoc.util.TestUtil; +import com.azure.core.util.Context; +import com.azure.storage.blob.*; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; +import org.apache.commons.io.FileUtils; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.neo4j.test.rule.DbmsRule; +import org.neo4j.test.rule.ImpermanentDbmsRule; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.UUID; + +import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED; +import static apoc.ApocConfig.APOC_IMPORT_FILE_USE_NEO4J_CONFIG; +import static apoc.ApocConfig.apocConfig; +import static apoc.load.LoadCsvTest.commonTestLoadCsv; +import static apoc.load.LoadHtmlTest.testLoadHtmlWithGetLinksCommon; +import static apoc.load.xls.LoadXlsTest.testLoadXlsCommon; +import static apoc.util.ExtendedITUtil.EXTENDED_PATH; +import static apoc.util.ExtendedITUtil.testLoadJsonCommon; +import static apoc.util.ExtendedITUtil.testLoadXmlCommon; +import static apoc.util.TestUtil.testResult; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + + +public class LoadAzureStorageTest extends AzureStorageBaseTest { + + + @ClassRule + public static DbmsRule db = new ImpermanentDbmsRule(); + + @BeforeClass + public static void setUp() throws Exception { + AzureStorageBaseTest.setUp(); + + TestUtil.registerProcedure(db, LoadCsv.class, LoadDirectory.class, LoadJson.class, LoadHtml.class, LoadXls.class, Xml.class); + apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); + apocConfig().setProperty(APOC_IMPORT_FILE_USE_NEO4J_CONFIG, false); + } + + + @Test + public void testLoadCsv() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/test.csv"); + commonTestLoadCsv(db, url); + } + + @Test + public void testLoadJson() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/map.json"); + testLoadJsonCommon(db, url); + } + + @Test + public void testLoadXml() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/xml/books.xml"); + testLoadXmlCommon(db, url); + } + + @Test + public void testLoadXls() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/load_test.xlsx"); + testLoadXlsCommon(db, url); + } + + @Test + public void testLoadHtml() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/wikipedia.html"); + testLoadHtmlWithGetLinksCommon(db, url); + } + +} diff --git a/extended-it/src/test/java/apoc/azure/ParquetAzureStorageTest.java b/extended-it/src/test/java/apoc/azure/ParquetAzureStorageTest.java new file mode 100644 index 0000000000..18d57c2b0f --- /dev/null +++ b/extended-it/src/test/java/apoc/azure/ParquetAzureStorageTest.java @@ -0,0 +1,61 @@ +package apoc.azure; + +import apoc.export.parquet.ParquetTestUtil; +import apoc.util.collection.Iterators; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.neo4j.test.rule.DbmsRule; +import org.neo4j.test.rule.ImpermanentDbmsRule; + +import java.io.File; +import java.util.Collections; +import java.util.Map; + +import static apoc.export.parquet.ParquetTest.MAPPING_ALL; +import static apoc.export.parquet.ParquetTestUtil.beforeClassCommon; +import static apoc.export.parquet.ParquetTestUtil.beforeCommon; +import static apoc.export.parquet.ParquetTestUtil.testImportAllCommon; +import static apoc.util.ExtendedITUtil.EXTENDED_PATH; +import static apoc.util.GoogleCloudStorageContainerExtension.gcsUrl; +import static apoc.util.TestUtil.testCall; +import static apoc.util.TestUtil.testResult; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ParquetAzureStorageTest extends AzureStorageBaseTest { + + private final String EXPORT_FILENAME = "test_all.parquet"; + + @ClassRule + public static DbmsRule db = new ImpermanentDbmsRule(); + + @BeforeClass + public static void beforeClass() { + beforeClassCommon(db); + } + + @Before + public void before() { + beforeCommon(db); + } + + @Test + public void testLoadParquet() { + String query = "CALL apoc.load.parquet($url, $config) YIELD value " + + "RETURN value"; + + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/" + EXPORT_FILENAME); + testResult(db, query, Map.of("url", url, "config", MAPPING_ALL), + ParquetTestUtil::roundtripLoadAllAssertions); + } + + @Test + public void testImportParquet() { + String url = putToAzureStorageAndGetUrl(EXTENDED_PATH + "src/test/resources/" + EXPORT_FILENAME); + + Map params = Map.of("file", url, "config", MAPPING_ALL); + testImportAllCommon(db, params); + } +} diff --git a/extended-it/src/test/java/apoc/export/ImportGoogleCloudStorageTest.java b/extended-it/src/test/java/apoc/gc/ImportGoogleCloudStorageTest.java similarity index 92% rename from extended-it/src/test/java/apoc/export/ImportGoogleCloudStorageTest.java rename to extended-it/src/test/java/apoc/gc/ImportGoogleCloudStorageTest.java index 496eeab7e0..9f06c87997 100644 --- a/extended-it/src/test/java/apoc/export/ImportGoogleCloudStorageTest.java +++ b/extended-it/src/test/java/apoc/gc/ImportGoogleCloudStorageTest.java @@ -1,4 +1,4 @@ -package apoc.export; +package apoc.gc; import apoc.export.arrow.ExportArrow; import apoc.export.arrow.ImportArrow; @@ -41,14 +41,14 @@ public static void setUp() throws Exception { @Test public void testImportArrow() { - String url = gcsUrl(gcs, "b/folder/o/test_all.arrow?alt=media"); + String url = gcsUrl(gcs, "test_all.arrow"); testImportCommon(db, url, MAPPING_ALL); } @Test public void testImportGexf() { clearDb(db); - String url = gcsUrl(gcs, "b/folder/o/data.gexf?alt=media"); + String url = gcsUrl(gcs, "data.gexf"); testImportGexfCommon(db, url); } diff --git a/extended-it/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java b/extended-it/src/test/java/apoc/gc/LoadGoogleCloudStorageTest.java similarity index 92% rename from extended-it/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java rename to extended-it/src/test/java/apoc/gc/LoadGoogleCloudStorageTest.java index 68f7de9a46..725ad2e659 100644 --- a/extended-it/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java +++ b/extended-it/src/test/java/apoc/gc/LoadGoogleCloudStorageTest.java @@ -1,5 +1,9 @@ -package apoc.load; +package apoc.gc; +import apoc.load.LoadCsv; +import apoc.load.LoadHtml; +import apoc.load.LoadJson; +import apoc.load.Xml; import apoc.load.xls.LoadXls; import apoc.util.GoogleCloudStorageContainerExtension; import apoc.util.TestUtil; @@ -56,7 +60,7 @@ public static void tearDown() { @Test public void testLoadCsv() { - String url = gcsUrl(gcs, "b/folder/o/test.csv?alt=media"); + String url = gcsUrl(gcs, "test.csv"); testResult(db, "CALL apoc.load.csv($url)", map("url", url), (r) -> { assertRow(r, "Selma", "8", 0L); @@ -68,7 +72,7 @@ public void testLoadCsv() { @Test public void testLoadJSON() { - String url = gcsUrl(gcs, "b/folder/o/map.json?alt=media"); + String url = gcsUrl(gcs, "map.json"); testCall(db, "CALL apoc.load.jsonArray($url, '$.foo')", map("url", url), (r) -> { assertEquals(asList(1L,2L,3L), r.get("value")); }); @@ -76,7 +80,7 @@ public void testLoadJSON() { @Test public void testLoadXml() { - String url = gcsUrl(gcs, "b/folder/o/books.xml?alt=media"); + String url = gcsUrl(gcs, "books.xml"); testCall(db, "CALL apoc.load.xml($url,'/catalog/book[title=\"Maeve Ascendant\"]/.',{failOnError:false}) yield value as result", Util.map("url", url), (r) -> { Object value = Iterables.single(r.values()); Assert.assertEquals(XmlTestUtils.XML_XPATH_AS_NESTED_MAP, value); @@ -85,7 +89,7 @@ public void testLoadXml() { @Test public void testLoadXls() { - String url = gcsUrl(gcs, "b/folder/o/load_test.xlsx?alt=media"); + String url = gcsUrl(gcs, "load_test.xlsx"); testResult(db, "CALL apoc.load.xls($url,'Full',{mapping:{Integer:{type:'int'}, Array:{type:'int',array:true,arraySep:';'}}})", map("url",url), // 'file:load_test.xlsx' (r) -> { assertXlsRow(r,0L,"String","Test","Boolean",true,"Integer",2L,"Float",1.5d,"Array",asList(1L,2L,3L)); @@ -95,7 +99,7 @@ public void testLoadXls() { @Test public void testLoadHtml() { - String url = gcsUrl(gcs, "b/folder/o/wikipedia.html?alt=media"); + String url = gcsUrl(gcs, "wikipedia.html"); Map query = map("links", "a[href]"); diff --git a/extended-it/src/test/java/apoc/gc/ParquetGoogleCloudStorageTest.java b/extended-it/src/test/java/apoc/gc/ParquetGoogleCloudStorageTest.java new file mode 100644 index 0000000000..e8e2ce5013 --- /dev/null +++ b/extended-it/src/test/java/apoc/gc/ParquetGoogleCloudStorageTest.java @@ -0,0 +1,69 @@ +package apoc.gc; + +import apoc.export.parquet.ParquetTestUtil; +import apoc.util.GoogleCloudStorageContainerExtension; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.neo4j.test.rule.DbmsRule; +import org.neo4j.test.rule.ImpermanentDbmsRule; + +import java.util.Map; + +import static apoc.ApocConfig.APOC_EXPORT_FILE_ENABLED; +import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED; +import static apoc.ApocConfig.apocConfig; +import static apoc.export.parquet.ParquetTest.MAPPING_ALL; +import static apoc.export.parquet.ParquetTestUtil.beforeClassCommon; +import static apoc.export.parquet.ParquetTestUtil.testImportAllCommon; +import static apoc.util.GoogleCloudStorageContainerExtension.gcsUrl; +import static apoc.util.TestUtil.testResult; + +public class ParquetGoogleCloudStorageTest { + + private static final String FILENAME = "test_all.parquet"; + + public static GoogleCloudStorageContainerExtension gcs; + + + @ClassRule + public static DbmsRule db = new ImpermanentDbmsRule(); + + @BeforeClass + public static void setUp() throws Exception { + apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); + apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true); + + beforeClassCommon(db); + + gcs = new GoogleCloudStorageContainerExtension() + .withMountedResourceFile(FILENAME, "/folder/" + FILENAME); + + gcs.start(); + } + + @AfterClass + public static void tearDown() { + gcs.close(); + db.shutdown(); + } + + @Test + public void testLoadParquet() { + String query = "CALL apoc.load.parquet($url, $config) YIELD value " + + "RETURN value"; + + String url = gcsUrl(gcs, FILENAME); + testResult(db, query, Map.of("url", url, "config", MAPPING_ALL), + ParquetTestUtil::roundtripLoadAllAssertions); + } + + @Test + public void testImportParquet() { + String url = gcsUrl(gcs, FILENAME); + + Map params = Map.of("file", url, "config", MAPPING_ALL); + testImportAllCommon(db, params); + } +} diff --git a/extended/src/test/java/apoc/export/ImportS3Test.java b/extended-it/src/test/java/apoc/s3/ImportS3Test.java similarity index 92% rename from extended/src/test/java/apoc/export/ImportS3Test.java rename to extended-it/src/test/java/apoc/s3/ImportS3Test.java index 115dbfc0a5..5330b075a7 100644 --- a/extended/src/test/java/apoc/export/ImportS3Test.java +++ b/extended-it/src/test/java/apoc/s3/ImportS3Test.java @@ -1,4 +1,4 @@ -package apoc.export; +package apoc.s3; import apoc.export.arrow.ExportArrow; import apoc.export.arrow.ImportArrow; @@ -24,6 +24,7 @@ import static apoc.export.arrow.ImportArrowTestUtil.MAPPING_ALL; import static apoc.export.arrow.ImportArrowTestUtil.prepareDbForArrow; import static apoc.export.arrow.ImportArrowTestUtil.testImportCommon; +import static apoc.util.ExtendedITUtil.EXTENDED_PATH; import static apoc.util.ExtendedTestUtil.clearDb; import static apoc.util.GexfTestUtil.testImportGexfCommon; import static apoc.util.s3.S3Util.putToS3AndGetUrl; @@ -61,8 +62,7 @@ public void testImportArrow() { @Test public void testImportGexf() { clearDb(db); - String file = Thread.currentThread().getContextClassLoader().getResource("gexf/data.gexf").toString(); - String filename = file.substring(file.indexOf("build")); + String filename = EXTENDED_PATH + "src/test/resources/gexf/data.gexf"; String url = putToS3AndGetUrl(s3Container, filename); testImportGexfCommon(db, url); } diff --git a/extended-it/src/test/java/apoc/s3/LoadS3Test.java b/extended-it/src/test/java/apoc/s3/LoadS3Test.java index 2a7b1a29a9..17551e06d0 100644 --- a/extended-it/src/test/java/apoc/s3/LoadS3Test.java +++ b/extended-it/src/test/java/apoc/s3/LoadS3Test.java @@ -7,39 +7,25 @@ import apoc.load.Xml; import apoc.load.xls.LoadXls; import apoc.util.TestUtil; -import apoc.util.Util; -import apoc.xml.XmlTestUtils; -import org.junit.Assert; +import apoc.util.s3.S3BaseTest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.jupiter.api.AfterAll; -import org.neo4j.driver.internal.util.Iterables; -import org.neo4j.graphdb.Result; import org.neo4j.test.rule.DbmsRule; import org.neo4j.test.rule.ImpermanentDbmsRule; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED; import static apoc.ApocConfig.APOC_IMPORT_FILE_USE_NEO4J_CONFIG; import static apoc.ApocConfig.apocConfig; -import static apoc.load.LoadCsvTest.assertRow; +import static apoc.load.LoadCsvTest.commonTestLoadCsv; +import static apoc.load.LoadHtmlTest.testLoadHtmlWithGetLinksCommon; +import static apoc.load.xls.LoadXlsTest.testLoadXlsCommon; import static apoc.util.ExtendedITUtil.EXTENDED_PATH; -import static apoc.util.MapUtil.map; -import static apoc.util.S3ExtendedUtil.putToS3AndGetUrl; -import static apoc.util.TestUtil.testCall; -import static apoc.util.TestUtil.testResult; -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static apoc.util.ExtendedITUtil.testLoadJsonCommon; +import static apoc.util.ExtendedITUtil.testLoadXmlCommon; +import static apoc.util.s3.S3Util.putToS3AndGetUrl; -public class LoadS3Test extends S3BaseExtendedTest { +public class LoadS3Test extends S3BaseTest { @Rule public DbmsRule db = new ImpermanentDbmsRule(); @@ -49,91 +35,36 @@ public void setUp() throws Exception { TestUtil.registerProcedure(db, LoadCsv.class, LoadDirectory.class, LoadJson.class, LoadHtml.class, LoadXls.class, Xml.class); apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); apocConfig().setProperty(APOC_IMPORT_FILE_USE_NEO4J_CONFIG, false); - putFolderToS3(); - } - - @AfterAll - public void tearDownAll() { - db.shutdown(); } @Test public void testLoadCsv() { - String url = putToS3AndGetUrl(s3ExtendedContainer, EXTENDED_PATH + "src/test/resources/test.csv"); - testResult(db, "CALL apoc.load.csv($url,{failOnError:false})", map("url", url), (r) -> { - assertRow(r, "Selma", "8", 0L); - assertRow(r, "Rana", "11", 1L); - assertRow(r, "Selina", "18", 2L); - assertFalse(r.hasNext()); - }); + String url = putToS3AndGetUrl(s3Container, EXTENDED_PATH + "src/test/resources/test.csv"); + commonTestLoadCsv(db, url); } - @Test public void testLoadJson() { - String url = putToS3AndGetUrl(s3ExtendedContainer, EXTENDED_PATH + "src/test/resources/map.json"); - testCall(db, "CALL apoc.load.json($url,'')",map("url", url), - (row) -> { - assertEquals(map("foo",asList(1L,2L,3L)), row.get("value")); - }); + @Test + public void testLoadJson() { + String url = putToS3AndGetUrl(s3Container, EXTENDED_PATH + "src/test/resources/map.json"); + testLoadJsonCommon(db, url); } - @Test public void testLoadXml() { - String url = putToS3AndGetUrl(s3ExtendedContainer, EXTENDED_PATH + "src/test/resources/xml/books.xml"); - testCall(db, "CALL apoc.load.xml($url,'/catalog/book[title=\"Maeve Ascendant\"]/.',{failOnError:false}) yield value as result", Util.map("url", url), (r) -> { - Object value = Iterables.single(r.values()); - Assert.assertEquals(XmlTestUtils.XML_XPATH_AS_NESTED_MAP, value); - }); + @Test + public void testLoadXml() { + String url = putToS3AndGetUrl(s3Container, EXTENDED_PATH + "src/test/resources/xml/books.xml"); + testLoadXmlCommon(db, url); } - @Test public void testLoadXls() { - String url = putToS3AndGetUrl(s3ExtendedContainer, EXTENDED_PATH + "src/test/resources/load_test.xlsx"); - testResult(db, "CALL apoc.load.xls($url,'Full',{mapping:{Integer:{type:'int'}, Array:{type:'int',array:true,arraySep:';'}}})", map("url",url), // 'file:load_test.xlsx' - (r) -> { - assertXlsRow(r,0L,"String","Test","Boolean",true,"Integer",2L,"Float",1.5d,"Array",asList(1L,2L,3L)); - assertFalse("Should not have another row",r.hasNext()); - }); + @Test + public void testLoadXls() { + String url = putToS3AndGetUrl(s3Container, EXTENDED_PATH + "src/test/resources/load_test.xlsx"); + testLoadXlsCommon(db, url); } @Test public void testLoadHtml() { - String url = putToS3AndGetUrl(s3ExtendedContainer, EXTENDED_PATH + "src/test/resources/wikipedia.html"); - - Map query = map("links", "a[href]"); - - testCall(db, "CALL apoc.load.html($url,$query)", - map("url", url, "query", query), - row -> { - final List> actual = (List) ((Map) row.get("value")).get("links"); - assertEquals(106, actual.size()); - assertTrue(actual.stream().allMatch(i -> i.get("tagName").equals("a"))); - }); - } - - private void putFolderToS3() { - StringBuilder csv= new StringBuilder(); // Faster - csv.append("name,age\r\n"); - csv.append("Bonzo,20\r\n"); - csv.append("Oronzo,45\r\n"); - byte[] data = csv.toString().getBytes(StandardCharsets.UTF_8); - - s3ExtendedContainer.putObjectToS3("test_folder/test.csv", data); - - csv = new StringBuilder(); - csv.append("name,age\r\n"); - csv.append("Bobby,18\r\n"); - csv.append("Maruccio,90\r\n"); - data = csv.toString().getBytes(StandardCharsets.UTF_8); - - s3ExtendedContainer.putObjectToS3("test_folder/test_1.csv", data); - } - - static void assertXlsRow(Result r, long lineNo, Object...data) { - Map row = r.next(); - Map map = map(data); - assertEquals(map, row.get("map")); - Map stringMap = new LinkedHashMap<>(map.size()); - map.forEach((k,v) -> stringMap.put(k,v == null ? null : v.toString())); - assertEquals(new ArrayList<>(map.values()), row.get("list")); - assertEquals(lineNo, row.get("lineNo")); + String url = putToS3AndGetUrl(s3Container, EXTENDED_PATH + "src/test/resources/wikipedia.html"); + testLoadHtmlWithGetLinksCommon(db, url); } } diff --git a/extended-it/src/test/java/apoc/s3/ParquetS3Test.java b/extended-it/src/test/java/apoc/s3/ParquetS3Test.java index 0f5da9a997..2c5374d196 100644 --- a/extended-it/src/test/java/apoc/s3/ParquetS3Test.java +++ b/extended-it/src/test/java/apoc/s3/ParquetS3Test.java @@ -2,6 +2,7 @@ import apoc.export.parquet.ParquetTestUtil; import apoc.util.collection.Iterators; +import apoc.util.s3.S3BaseTest; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -16,13 +17,13 @@ import static apoc.export.parquet.ParquetTest.MAPPING_ALL; import static apoc.export.parquet.ParquetTestUtil.beforeClassCommon; import static apoc.export.parquet.ParquetTestUtil.beforeCommon; -import static apoc.util.S3ExtendedUtil.putToS3AndGetUrl; import static apoc.util.TestUtil.testCall; import static apoc.util.TestUtil.testResult; +import static apoc.util.s3.S3Util.putToS3AndGetUrl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -public class ParquetS3Test extends S3BaseExtendedTest { +public class ParquetS3Test extends S3BaseTest { private final String EXPORT_FILENAME = "test_all.parquet"; @@ -60,7 +61,7 @@ public void testFileRoundtripParquetAll() { public void testFileRoundtripParquetAllFromS3Url() { // given - when String filename = exportToParquetFile(EXPORT_FILENAME); - String url = putToS3AndGetUrl(s3ExtendedContainer, filename); + String url = putToS3AndGetUrl(s3Container, filename); // then final String query = "CALL apoc.load.parquet($url, $config) YIELD value " + @@ -75,7 +76,7 @@ public void testFileRoundtripParquetAllFromS3Url() { @Test public void testImportParquetFromS3Url() { String filename = exportToParquetFile(EXPORT_FILENAME); - String url = putToS3AndGetUrl(s3ExtendedContainer, filename); + String url = putToS3AndGetUrl(s3Container, filename); db.executeTransactionally("MATCH (n) DETACH DELETE n"); Long count = db.executeTransactionally("MATCH (n) RETURN count(n) AS count", Collections.emptyMap(), diff --git a/extended-it/src/test/java/apoc/s3/S3BaseExtendedTest.java b/extended-it/src/test/java/apoc/s3/S3BaseExtendedTest.java deleted file mode 100644 index fbe51a7e3e..0000000000 --- a/extended-it/src/test/java/apoc/s3/S3BaseExtendedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package apoc.s3; - -import apoc.util.S3ExtendedContainer; -import org.junit.AfterClass; -import org.junit.BeforeClass; - -public abstract class S3BaseExtendedTest { - protected static S3ExtendedContainer s3ExtendedContainer; - - @BeforeClass - public static void baseBeforeClass() { - s3ExtendedContainer = new S3ExtendedContainer(); - - // In test environment we skip the MD5 validation that can cause issues - System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "true"); - System.setProperty("com.amazonaws.sdk.disableCertChecking", "true"); - } - - @AfterClass - public static void tearDown() { - System.clearProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation"); - System.clearProperty("com.amazonaws.sdk.disableCertChecking"); - - s3ExtendedContainer.close(); - } -} diff --git a/extended-it/src/test/java/apoc/util/ExtendedITUtil.java b/extended-it/src/test/java/apoc/util/ExtendedITUtil.java index 9cde09d966..8e54d5c8a1 100644 --- a/extended-it/src/test/java/apoc/util/ExtendedITUtil.java +++ b/extended-it/src/test/java/apoc/util/ExtendedITUtil.java @@ -1,5 +1,29 @@ package apoc.util; +import apoc.xml.XmlTestUtils; +import org.junit.Assert; +import org.neo4j.driver.internal.util.Iterables; +import org.neo4j.graphdb.GraphDatabaseService; + +import static apoc.util.MapUtil.map; +import static apoc.util.TestUtil.testCall; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + public class ExtendedITUtil { public static final String EXTENDED_PATH = "../extended/"; + + public static void testLoadXmlCommon(GraphDatabaseService db, String url) { + testCall(db, "CALL apoc.load.xml($url,'/catalog/book[title=\"Maeve Ascendant\"]/.',{failOnError:false}) yield value as result", Util.map("url", url), (r) -> { + Object value = Iterables.single(r.values()); + Assert.assertEquals(XmlTestUtils.XML_XPATH_AS_NESTED_MAP, value); + }); + } + + public static void testLoadJsonCommon(GraphDatabaseService db, String url) { + testCall(db, "CALL apoc.load.json($url, '')",map("url", url), + (row) -> { + assertEquals(map("foo",asList(1L,2L,3L)), row.get("value")); + }); + } } diff --git a/extended-it/src/test/java/apoc/util/GoogleCloudStorageContainerExtension.java b/extended-it/src/test/java/apoc/util/GoogleCloudStorageContainerExtension.java index 4a5ca7be3d..d4a6725db7 100644 --- a/extended-it/src/test/java/apoc/util/GoogleCloudStorageContainerExtension.java +++ b/extended-it/src/test/java/apoc/util/GoogleCloudStorageContainerExtension.java @@ -25,7 +25,8 @@ public GoogleCloudStorageContainerExtension withMountedResourceFile(String resou return this; } - public static String gcsUrl(GoogleCloudStorageContainerExtension gcs, String path) { + public static String gcsUrl(GoogleCloudStorageContainerExtension gcs, String file) { + String path = "b/folder/o/%s?alt=media".formatted(file); return String.format("http://%s:%d/storage/v1/%s", gcs.getContainerIpAddress(), gcs.getMappedPort(4443), path); } } diff --git a/extended-it/src/test/java/apoc/util/S3ExtendedContainer.java b/extended-it/src/test/java/apoc/util/S3ExtendedContainer.java deleted file mode 100644 index 07eba1417c..0000000000 --- a/extended-it/src/test/java/apoc/util/S3ExtendedContainer.java +++ /dev/null @@ -1,120 +0,0 @@ -package apoc.util; - -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; - -import org.apache.commons.lang3.tuple.Pair; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; -import java.net.URI; -import java.util.List; - -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.S3ObjectSummary; -import org.testcontainers.containers.localstack.LocalStackContainer; -import org.testcontainers.utility.DockerImageName; - -public class S3ExtendedContainer implements AutoCloseable { - private static final String S3_BUCKET_NAME = "test-bucket"; - private final LocalStackContainer localstack; - private final AmazonS3 s3; - - public S3ExtendedContainer() { - localstack = new LocalStackContainer(DockerImageName.parse("localstack/localstack:1.2.0")).withServices(S3); - localstack.addExposedPorts(4566); - localstack.start(); - - s3 = AmazonS3ClientBuilder.standard() - .withEndpointConfiguration(getEndpointConfiguration()) - .withCredentials(getCredentialsProvider()) - .build(); - s3.createBucket(S3_BUCKET_NAME); - } - - public void close() { - Util.close(localstack); - } - - public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { - return new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpoint().toString(), localstack.getRegion()); - } - - public AWSCredentialsProvider getCredentialsProvider() { - return new AWSStaticCredentialsProvider( - new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey())); - } - - public String getUrl(String key) { - return String.format( - "s3://%s.%s/%s/%s?accessKey=%s&secretKey=%s", - getEndpointConfiguration().getSigningRegion(), - getEndpointConfiguration().getServiceEndpoint().replace("http://", ""), - S3_BUCKET_NAME, - key, - getCredentialsProvider().getCredentials().getAWSAccessKeyId(), - getCredentialsProvider().getCredentials().getAWSSecretKey()); - } - - @SuppressWarnings("unused") // used from extended - public String putFile(String fileName) { - final File file = new File(fileName); - s3.putObject(S3_BUCKET_NAME, file.getName(), file); - return getUrl(file.getName()); - } - - public List listBucket(String bucketName, String prefix) { - ObjectListing listing = s3.listObjects( bucketName, prefix ); - List summaries = listing.getObjectSummaries(); - - while (listing.isTruncated()) { - listing = s3.listNextBatchOfObjects (listing); - summaries.addAll (listing.getObjectSummaries()); - } - - return summaries; - } - - public void putObjectToS3(String key, byte[] data) { - InputStream is = new ByteArrayInputStream(data); - - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentLength(data.length); - - PutObjectRequest putObjectRequest = new PutObjectRequest(S3_BUCKET_NAME, key, is, metadata); - - try { - s3.putObject(putObjectRequest); - } catch (Exception e) { - System.out.println("Error Message: " + e.getMessage()); - } - } - - public Pair parseS3URI(String url) { - try { - var uri = new URI(url); - - if ("s3".equals(uri.getScheme())) { - var bucket = uri.getPath().split("/")[1]; - var key = uri.getPath().split("/")[2]; - - return Pair.of(bucket, key); - } - - return null; - } - catch (Exception ex) { - throw new RuntimeException(ex.getMessage()); - } - } -} - diff --git a/extended-it/src/test/java/apoc/util/S3ExtendedUtil.java b/extended-it/src/test/java/apoc/util/S3ExtendedUtil.java deleted file mode 100644 index 992e7a1106..0000000000 --- a/extended-it/src/test/java/apoc/util/S3ExtendedUtil.java +++ /dev/null @@ -1,12 +0,0 @@ -package apoc.util; - -public class S3ExtendedUtil { - public static String removeRegionFromUrl(S3ExtendedContainer s3ExtendedContainer, String url) { - return url.replace(s3ExtendedContainer.getEndpointConfiguration().getSigningRegion() + ".", ""); - } - - public static String putToS3AndGetUrl(S3ExtendedContainer s3ExtendedContainer, String filename) { - String url = s3ExtendedContainer.putFile(filename); - return removeRegionFromUrl(s3ExtendedContainer, url); - } -} diff --git a/extended-it/test_all.parquet b/extended-it/test_all.parquet new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extended/src/main/java/apoc/vectordb/VectorMappingConfig.java b/extended/src/main/java/apoc/vectordb/VectorMappingConfig.java index 3850544179..2de755dbbb 100644 --- a/extended/src/main/java/apoc/vectordb/VectorMappingConfig.java +++ b/extended/src/main/java/apoc/vectordb/VectorMappingConfig.java @@ -3,6 +3,8 @@ import java.util.Collections; import java.util.Map; + + public class VectorMappingConfig { enum MappingMode { READ_ONLY, UPDATE_EXISTING, CREATE_IF_MISSING diff --git a/extended/src/test/java/apoc/export/parquet/ParquetTestUtil.java b/extended/src/test/java/apoc/export/parquet/ParquetTestUtil.java index e760e687f5..6cc6d0f1f4 100644 --- a/extended/src/test/java/apoc/export/parquet/ParquetTestUtil.java +++ b/extended/src/test/java/apoc/export/parquet/ParquetTestUtil.java @@ -41,6 +41,9 @@ public class ParquetTestUtil { public static void beforeClassCommon(GraphDatabaseService db) { TestUtil.registerProcedure(db, ExportParquet.class, ImportParquet.class, LoadParquet.class, Graphs.class, Meta.class); + + apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); + apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true); } public static void beforeCommon(GraphDatabaseService db) { @@ -48,9 +51,6 @@ public static void beforeCommon(GraphDatabaseService db) { db.executeTransactionally("CREATE (f:User {name:'Adam',age:42,male:true,kids:['Sam','Anna','Grace', 'Qwe'], born:localdatetime('2015-05-18T19:32:24.000'), place:point({latitude: 13.1, longitude: 33.46789, height: 100.0})})-[:KNOWS {since: 1993, bffSince: duration('P5M1.5D')}]->(b:User {name:'Jim',age:42})"); db.executeTransactionally("CREATE (:Another {foo:1, listDate: [date('1999'), date('2000')], listInt: [1,2]}), (:Another {bar:'Sam'})"); - - apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true); - apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true); } public static void testImportAllCommon(GraphDatabaseService db, Map params) { diff --git a/extended/src/test/java/apoc/load/LoadCsvTest.java b/extended/src/test/java/apoc/load/LoadCsvTest.java index 51ed709762..c8e77d04fb 100644 --- a/extended/src/test/java/apoc/load/LoadCsvTest.java +++ b/extended/src/test/java/apoc/load/LoadCsvTest.java @@ -11,6 +11,7 @@ import org.mockserver.integration.ClientAndServer; import org.mockserver.model.Header; import org.neo4j.configuration.GraphDatabaseSettings; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.Result; import org.neo4j.test.rule.DbmsRule; @@ -79,8 +80,7 @@ public LoadCsvTest() throws URISyntaxException { @Test public void testLoadCsv() throws Exception { String url = "test.csv"; - testResult(db, "CALL apoc.load.csv($url,{results:['map','list','stringMap','strings']})", map("url",url), // 'file:test.csv' - this::commonAssertionsLoadCsv); + commonTestLoadCsv(db, url); } @Test @@ -88,7 +88,7 @@ public void testLoadCsvWithBinary() { testResult(db, "CALL apoc.load.csvParams($file, null, null, $conf)", map("file", fileToBinary(new File(getUrlFileName("test.csv").getPath()), CompressionAlgo.DEFLATE.name()), "conf", map(COMPRESSION, CompressionAlgo.DEFLATE.name(), "results", List.of("map", "list", "stringMap", "strings"))), - this::commonAssertionsLoadCsv); + LoadCsvTest::commonAssertionsLoadCsv); } @Test @@ -128,11 +128,11 @@ public void testLoadCsvWithMultiCharSeparator(){ "sep", "SEP"); testResult(db, "CALL apoc.load.csv($url, $conf)", map("url",url, "conf", conf), - this::commonAssertionsLoadCsv); + LoadCsvTest::commonAssertionsLoadCsv); } - private void commonAssertionsLoadCsv(Result r) { + private static void commonAssertionsLoadCsv(Result r) { assertRow(r, 0L, "name", "Selma", "age", "8"); assertRow(r, 1L, "name", "Rana", "age", "11"); assertRow(r, 2L, "name", "Selina", "age", "18"); @@ -775,4 +775,9 @@ private static String fromListOfMapToCsvString(List> mapList .withHeader()) .writeValueAsString(mapList); } + + public static void commonTestLoadCsv(GraphDatabaseService db, String url) { + testResult(db, "CALL apoc.load.csv($url,{results:['map','list','stringMap','strings']})", map("url", url), // 'file:test.csv' + LoadCsvTest::commonAssertionsLoadCsv); + } } diff --git a/extended/src/test/java/apoc/load/LoadHtmlTest.java b/extended/src/test/java/apoc/load/LoadHtmlTest.java index b3c4071797..153674d8d1 100644 --- a/extended/src/test/java/apoc/load/LoadHtmlTest.java +++ b/extended/src/test/java/apoc/load/LoadHtmlTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.jupiter.api.AfterAll; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.test.rule.DbmsRule; import org.neo4j.test.rule.ImpermanentDbmsRule; @@ -260,15 +261,8 @@ private void loadHtmlWithSelector(int expected, String selector) { @Test public void testQueryMetadataWithGetLinks() { - Map query = map("links", "a[href]"); - - testCall(db, "CALL apoc.load.html($url,$query)", - map("url", new File("src/test/resources/wikipedia.html").toURI().toString(), "query", query), - row -> { - final List> actual = (List) ((Map) row.get("value")).get("links"); - assertEquals(106, actual.size()); - assertTrue(actual.stream().allMatch(i -> i.get("tagName").equals("a"))); - }); + String url = new File("src/test/resources/wikipedia.html").toURI().toString(); + testLoadHtmlWithGetLinksCommon(db, url); } @Test @@ -527,6 +521,18 @@ private void testCallGeneratedJsWithBrowser(String browser) { }); }); } + + public static void testLoadHtmlWithGetLinksCommon(GraphDatabaseService db, String url) { + Map query = map("links", "a[href]"); + + testCall(db, "CALL apoc.load.html($url,$query)", + map("url", url, "query", query), + row -> { + final List> actual = (List) ((Map) row.get("value")).get("links"); + assertEquals(106, actual.size()); + assertTrue(actual.stream().allMatch(i -> i.get("tagName").equals("a"))); + }); + } public static void skipIfBrowserNotPresentOrCompatible(Runnable runnable) { try { diff --git a/extended/src/test/java/apoc/load/xls/LoadXlsTest.java b/extended/src/test/java/apoc/load/xls/LoadXlsTest.java index 87636df48d..dec54f8aa4 100644 --- a/extended/src/test/java/apoc/load/xls/LoadXlsTest.java +++ b/extended/src/test/java/apoc/load/xls/LoadXlsTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import org.junit.jupiter.api.AfterAll; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.Result; import org.neo4j.test.rule.DbmsRule; @@ -57,12 +58,9 @@ public void tearDown() { } @Test public void testLoadXls() throws Exception { - testResult(db, "CALL apoc.load.xls($url,'Full',{mapping:{Integer:{type:'int'}, Array:{type:'int',array:true,arraySep:';'}}})", map("url",loadTest), // 'file:load_test.xlsx' - (r) -> { - assertRow(r,0L,"String","Test","Boolean",true,"Integer",2L,"Float",1.5d,"Array",asList(1L,2L,3L)); - assertFalse("Should not have another row",r.hasNext()); - }); + testLoadXlsCommon(db, loadTest); } + @Test public void testLoadBrokenHeader() throws Exception { BiFunction query = (sheet,header) -> db.executeTransactionally( "CALL apoc.load.xls($url,$sheet,{header:$header}) yield map return count(*) as c", @@ -515,4 +513,13 @@ private void assertIssue2403Excel(Result r, assertFalse(r.hasNext()); } + public static void testLoadXlsCommon(GraphDatabaseService db, String url) { + testResult(db, "CALL apoc.load.xls($url,'Full',{mapping:{Integer:{type:'int'}, Array:{type:'int',array:true,arraySep:';'}}})", + map("url",url), // 'file:load_test.xlsx' + (r) -> { + assertRow(r,0L,"String","Test","Boolean",true,"Integer",2L,"Float",1.5d,"Array",asList(1L,2L,3L)); + assertFalse("Should not have another row",r.hasNext()); + }); + } + } diff --git a/extended/src/test/resources/test_all.parquet b/extended/src/test/resources/test_all.parquet new file mode 100644 index 0000000000..430cfdf88b Binary files /dev/null and b/extended/src/test/resources/test_all.parquet differ