From c8fbeadd41e59552f1c714d070513260bbd8a5c7 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Mon, 20 Dec 2021 16:39:11 +0100
Subject: [PATCH] Added some amqp test

---
 Makefile                                      |  13 +-
 .../database/table/columns/TableColumn.java   |   6 +-
 fda-table-service/pom.xml                     |   8 +-
 .../src/test/java/at/tuwien/BaseUnitTest.java |  43 +++-
 .../java/at/tuwien/config/DockerConfig.java   |   9 +
 .../java/at/tuwien/config/MariaDbConfig.java  |  16 +-
 .../DataEndpointQueueIntegrationTest.java     | 203 ++++++++++++++++++
 ...a => DataEndpointRestIntegrationTest.java} |   3 +-
 .../endpoint/TableEndpointUnitTest.java       |  13 +-
 .../service/DataServiceIntegrationTest.java   |  43 ++--
 .../src/test/resources/application.properties |   1 +
 .../src/test/resources/species/Species.sql    |   6 +-
 .../src/test/resources/weather/WeatherAus.sql |  18 +-
 .../java/at/tuwien/mapper/TableMapper.java    |  15 +-
 .../at/tuwien/service/impl/JdbcConnector.java |  32 ++-
 .../tuwien/service/impl/MariaDataService.java |   9 +-
 .../tuwien/service/impl/RabbitMqService.java  |   2 +
 17 files changed, 359 insertions(+), 81 deletions(-)
 create mode 100644 fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointQueueIntegrationTest.java
 rename fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/{DataEndpointIntegrationTest.java => DataEndpointRestIntegrationTest.java} (98%)

diff --git a/Makefile b/Makefile
index 7552d2b4aa..3f1b6cf5c8 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,7 @@ config-docker:
 	docker image pull -q mysql:8.0 || true > /dev/null
 	docker image pull -q mariadb:10.5 || true > /dev/null
 	docker image pull -q rabbitmq:3-alpine || true > /dev/null
+	docker image pull -q nginx:1.20-alpine || true > /dev/null
 
 config: config-backend config-docker config-frontend
 
@@ -181,7 +182,17 @@ registry-staging: registry-staging-tag registry-staging-push
 logs:
 	docker-compose -f docker-compose.prod.yml logs
 
-clean:
+clean-maven:
+	mvn -f ./fda-authentication-service/pom.xml clean
+	#mvn -f ./fda-citation-service/pom.xml clean
+	mvn -f ./fda-container-service/pom.xml clean
+	mvn -f ./fda-database-service/pom.xml clean
+	mvn -f ./fda-discovery-service/pom.xml clean
+	mvn -f ./fda-gateway-service/pom.xml clean
+	mvn -f ./fda-query-service/pom.xml clean
+	mvn -f ./fda-table-service/pom.xml clean
+
+clean: clean-maven
 	docker-compose down || true
 	docker volume rm fda-services_fda-metadata-db-data || true
 	docker volume rm fda-public || true
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java
index 100c478285..4089b6fce0 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java
@@ -22,7 +22,7 @@ import java.util.List;
 @EntityListeners(AuditingEntityListener.class)
 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
 @javax.persistence.Table(name = "mdb_columns")
-public class TableColumn {
+public class TableColumn implements Comparable<TableColumn> {
 
     @Id
     @EqualsAndHashCode.Include
@@ -104,4 +104,8 @@ public class TableColumn {
     @LastModifiedDate
     private Instant lastModified;
 
+    @Override
+    public int compareTo(TableColumn tableColumn) {
+        return Integer.compare(this.ordinalPosition, tableColumn.getOrdinalPosition());
+    }
 }
diff --git a/fda-table-service/pom.xml b/fda-table-service/pom.xml
index f99ca04ee7..df29f30282 100644
--- a/fda-table-service/pom.xml
+++ b/fda-table-service/pom.xml
@@ -34,7 +34,7 @@
         <opencsv.version>5.4</opencsv.version>
         <super-csv.version>2.4.0</super-csv.version>
         <btrace-client.version>2.2.0</btrace-client.version>
-        <unleash.version>5.0.1</unleash.version>
+        <random-utils.version>2.0.0</random-utils.version>
     </properties>
 
     <dependencies>
@@ -101,6 +101,12 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.aventrix.jnanoid</groupId>
+            <artifactId>jnanoid</artifactId>
+            <scope>test</scope>
+            <version>${random-utils.version}</version>
+        </dependency>
         <!-- AMPQ -->
         <dependency>
             <groupId>com.rabbitmq</groupId>
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java
index 037ef13738..63fdb2bb96 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java
@@ -1,7 +1,5 @@
 package at.tuwien;
 
-import at.tuwien.api.container.image.ImageEnvItemDto;
-import at.tuwien.api.container.image.ImageEnvItemTypeDto;
 import at.tuwien.api.database.table.TableCreateDto;
 import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
@@ -23,6 +21,13 @@ import static java.time.temporal.ChronoUnit.HOURS;
 @TestPropertySource(locations = "classpath:application.properties")
 public abstract class BaseUnitTest extends CsvUnitTest {
 
+    public final static String DATABASE_NET = "fda-userdb";
+
+    public final static String BROKER_IMAGE = "fda-broker-service:latest";
+    public final static String BROKER_INTERNALNAME = "fda-broker-service";
+    public final static String BROKER_NET = "fda-public";
+    public final static String BROKER_IP = "172.29.0.2";
+
     public final static Long DATABASE_1_ID = 1L;
     public final static String DATABASE_1_NAME = "Weather";
     public final static String DATABASE_1_INTERNALNAME = "weather";
@@ -60,7 +65,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_1_1_ORDINALPOS = 0;
     public final static Boolean COLUMN_1_1_PRIMARY = true;
     public final static String COLUMN_1_1_NAME = "id";
-    public final static String COLUMN_1_1_INTERNAL_NAME = "mdb_id";
+    public final static String COLUMN_1_1_INTERNAL_NAME = "id";
     public final static TableColumnType COLUMN_1_1_TYPE = TableColumnType.NUMBER;
     public final static ColumnTypeDto COLUMN_1_1_TYPE_DTO = ColumnTypeDto.NUMBER;
     public final static Boolean COLUMN_1_1_NULL = false;
@@ -74,7 +79,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_1_2_ORDINALPOS = 1;
     public final static Boolean COLUMN_1_2_PRIMARY = false;
     public final static String COLUMN_1_2_NAME = "Date";
-    public final static String COLUMN_1_2_INTERNAL_NAME = "mdb_date";
+    public final static String COLUMN_1_2_INTERNAL_NAME = "date";
     public final static TableColumnType COLUMN_1_2_TYPE = TableColumnType.DATE;
     public final static ColumnTypeDto COLUMN_1_2_TYPE_DTO = ColumnTypeDto.DATE;
     public final static Boolean COLUMN_1_2_NULL = true;
@@ -88,7 +93,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_1_3_ORDINALPOS = 2;
     public final static Boolean COLUMN_1_3_PRIMARY = false;
     public final static String COLUMN_1_3_NAME = "Location";
-    public final static String COLUMN_1_3_INTERNAL_NAME = "mdb_location";
+    public final static String COLUMN_1_3_INTERNAL_NAME = "location";
     public final static TableColumnType COLUMN_1_3_TYPE = TableColumnType.STRING;
     public final static ColumnTypeDto COLUMN_1_3_TYPE_DTO = ColumnTypeDto.STRING;
     public final static Boolean COLUMN_1_3_NULL = true;
@@ -102,7 +107,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_1_4_ORDINALPOS = 3;
     public final static Boolean COLUMN_1_4_PRIMARY = false;
     public final static String COLUMN_1_4_NAME = "MinTemp";
-    public final static String COLUMN_1_4_INTERNAL_NAME = "mdb_mintemp";
+    public final static String COLUMN_1_4_INTERNAL_NAME = "mintemp";
     public final static TableColumnType COLUMN_1_4_TYPE = TableColumnType.STRING;
     public final static ColumnTypeDto COLUMN_1_4_TYPE_DTO = ColumnTypeDto.STRING;
     public final static Boolean COLUMN_1_4_NULL = true;
@@ -116,7 +121,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_1_5_ORDINALPOS = 4;
     public final static Boolean COLUMN_1_5_PRIMARY = false;
     public final static String COLUMN_1_5_NAME = "Rainfall";
-    public final static String COLUMN_1_5_INTERNAL_NAME = "mdb_rainfall";
+    public final static String COLUMN_1_5_INTERNAL_NAME = "rainfall";
     public final static TableColumnType COLUMN_1_5_TYPE = TableColumnType.NUMBER;
     public final static ColumnTypeDto COLUMN_1_5_TYPE_DTO = ColumnTypeDto.NUMBER;
     public final static Boolean COLUMN_1_5_NULL = true;
@@ -130,7 +135,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_3_1_ORDINALPOS = 0;
     public final static Boolean COLUMN_3_1_PRIMARY = false;
     public final static String COLUMN_3_1_NAME = "qu";
-    public final static String COLUMN_3_1_INTERNAL_NAME = "mdb_qu";
+    public final static String COLUMN_3_1_INTERNAL_NAME = "qu";
     public final static TableColumnType COLUMN_3_1_TYPE = TableColumnType.STRING;
     public final static ColumnTypeDto COLUMN_3_1_TYPE_DTO = ColumnTypeDto.STRING;
     public final static Boolean COLUMN_3_1_NULL = false;
@@ -144,7 +149,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_3_2_ORDINALPOS = 1;
     public final static Boolean COLUMN_3_2_PRIMARY = false;
     public final static String COLUMN_3_2_NAME = "species";
-    public final static String COLUMN_3_2_INTERNAL_NAME = "mdb_species";
+    public final static String COLUMN_3_2_INTERNAL_NAME = "species";
     public final static TableColumnType COLUMN_3_2_TYPE = TableColumnType.ENUM;
     public final static ColumnTypeDto COLUMN_3_2_TYPE_DTO = ColumnTypeDto.ENUM;
     public final static Boolean COLUMN_3_2_NULL = false;
@@ -158,7 +163,7 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static Integer COLUMN_3_3_ORDINALPOS = 2;
     public final static Boolean COLUMN_3_3_PRIMARY = false;
     public final static String COLUMN_3_3_NAME = "score";
-    public final static String COLUMN_3_3_INTERNAL_NAME = "mdb_score";
+    public final static String COLUMN_3_3_INTERNAL_NAME = "score";
     public final static TableColumnType COLUMN_3_3_TYPE = TableColumnType.STRING;
     public final static ColumnTypeDto COLUMN_3_3_TYPE_DTO = ColumnTypeDto.STRING;
     public final static Boolean COLUMN_3_3_NULL = false;
@@ -283,6 +288,16 @@ public abstract class BaseUnitTest extends CsvUnitTest {
     public final static String CONTAINER_3_IP = "172.28.0.7";
     public final static Instant CONTAINER_3_CREATED = Instant.now().minus(1, HOURS);
 
+    public final static Long CONTAINER_NGINX_ID = 4L;
+    public final static String CONTAINER_NGINX_HASH = "deadbeef";
+    public final static String CONTAINER_NGINX_IMAGE = "nginx";
+    public final static String CONTAINER_NGINX_TAG = "1.20-alpine";
+    public final static String CONTAINER_NGINX_NET = "fda-public";
+    public final static String CONTAINER_NGINX_NAME = "file-service";
+    public final static String CONTAINER_NGINX_INTERNALNAME = "fda-test-file-service";
+    public final static String CONTAINER_NGINX_IP = "172.29.0.3";
+    public final static Instant CONTAINER_NGINX_CREATED = Instant.now().minus(3, HOURS);
+
     public final static Container CONTAINER_1 = Container.builder()
             .id(CONTAINER_1_ID)
             .name(CONTAINER_1_NAME)
@@ -310,6 +325,14 @@ public abstract class BaseUnitTest extends CsvUnitTest {
             .containerCreated(CONTAINER_3_CREATED)
             .build();
 
+    public final static Container CONTAINER_NGINX = Container.builder()
+            .id(CONTAINER_NGINX_ID)
+            .name(CONTAINER_NGINX_NAME)
+            .internalName(CONTAINER_NGINX_INTERNALNAME)
+            .hash(CONTAINER_NGINX_HASH)
+            .containerCreated(CONTAINER_NGINX_CREATED)
+            .build();
+
     public final static List<TableColumn> TABLE_3_COLUMNS = List.of(TableColumn.builder()
                     .id(COLUMN_3_1_ID)
                     .ordinalPosition(COLUMN_3_1_ORDINALPOS)
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java b/fda-table-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java
index 057e57d64d..81d5bc8afe 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java
@@ -44,11 +44,20 @@ public class DockerConfig {
         dockerClient.startContainerCmd(container.getHash())
                 .exec();
         Thread.sleep(12 * 1000L);
+        log.debug("container {} was started", container.getHash());
     }
 
     public static void stopContainer(Container container) {
+        final InspectContainerResponse inspect = dockerClient.inspectContainerCmd(container.getHash())
+                .exec();
+        log.trace("container {} state {}", container.getHash(), inspect.getState().getStatus());
+        if (!Objects.equals(inspect.getState().getStatus(), "running")) {
+            return;
+        }
+        log.trace("container {} needs to be stopped", container.getHash());
         dockerClient.stopContainerCmd(container.getHash())
                 .exec();
+        log.debug("container {} was stopped", container.getHash());
     }
 
 }
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java b/fda-table-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java
index 39926a27fc..9ebb89afcf 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java
@@ -5,10 +5,7 @@ import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Configurable;
 import org.springframework.context.annotation.Bean;
 
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
 import java.util.Properties;
 
 @Log4j2
@@ -32,4 +29,15 @@ public class MariaDbConfig {
         connection.close();
     }
 
+    public static boolean contains(Table table, String column, Object expected) throws SQLException {
+        final String jdbc = "jdbc:mariadb://" + table.getDatabase().getContainer().getInternalName() + "/" + table.getDatabase().getInternalName();
+        log.trace("connect to database {}", jdbc);
+        final Connection connection = DriverManager.getConnection(jdbc, "mariadb", "mariadb");
+        final Statement statement = connection.createStatement();
+        final ResultSet result = statement.executeQuery("SELECT `" + column + "` FROM " + table.getInternalName() +
+                " WHERE `" + column + "` = " + expected.toString() + ";");
+        connection.close();
+        return result.next();
+    }
+
 }
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointQueueIntegrationTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointQueueIntegrationTest.java
new file mode 100644
index 0000000000..688da3bc5f
--- /dev/null
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointQueueIntegrationTest.java
@@ -0,0 +1,203 @@
+package at.tuwien.endpoint;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.database.table.TableCsvDto;
+import at.tuwien.config.DockerConfig;
+import at.tuwien.config.MariaDbConfig;
+import at.tuwien.config.ReadyConfig;
+import at.tuwien.endpoints.DataEndpoint;
+import at.tuwien.entities.container.Container;
+import at.tuwien.exception.*;
+import at.tuwien.repository.jpa.ImageRepository;
+import at.tuwien.repository.jpa.TableRepository;
+import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
+import com.github.dockerjava.api.command.CreateContainerResponse;
+import com.github.dockerjava.api.exception.NotModifiedException;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.Network;
+import com.rabbitmq.client.Channel;
+import lombok.extern.log4j.Log4j2;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.transaction.Transactional;
+import java.io.File;
+import java.sql.SQLException;
+import java.util.*;
+
+import static at.tuwien.config.DockerConfig.dockerClient;
+import static at.tuwien.config.DockerConfig.hostConfig;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+@Log4j2
+public class DataEndpointQueueIntegrationTest extends BaseUnitTest {
+
+    @MockBean
+    private Channel channel;
+
+    @MockBean
+    private ReadyConfig readyConfig;
+
+    @Autowired
+    private DataEndpoint dataEndpoint;
+
+    @Autowired
+    private TableRepository tableRepository;
+
+    @Autowired
+    private ImageRepository imageRepository;
+
+    /**
+     * We need a container to test the CRUD operations as of now it is unfeasible to determine the correctness of the
+     * operations without a live container
+     *
+     * @throws InterruptedException Sleep interrupted.
+     */
+    @BeforeAll
+    public static void beforeAll() throws InterruptedException {
+        afterAll();
+        /* create networks */
+        dockerClient.createNetworkCmd()
+                .withName(BROKER_NET)
+                .withIpam(new Network.Ipam()
+                        .withConfig(new Network.Ipam.Config()
+                                .withSubnet("172.29.0.0/16")))
+                .withEnableIpv6(false)
+                .exec();
+        dockerClient.createNetworkCmd()
+                .withName(DATABASE_NET)
+                .withIpam(new Network.Ipam()
+                        .withConfig(new Network.Ipam.Config()
+                                .withSubnet("172.28.0.0/16")))
+                .withEnableIpv6(false)
+                .exec();
+        /* create broker container */
+        final CreateContainerResponse response1 = dockerClient.createContainerCmd(BROKER_IMAGE)
+                .withHostConfig(hostConfig.withNetworkMode(BROKER_NET))
+                .withName(BROKER_INTERNALNAME)
+                .withIpv4Address(BROKER_IP)
+                .withHostName(BROKER_INTERNALNAME)
+                .withEnv("TZ=Europe/Vienna")
+                .exec();
+        dockerClient.startContainerCmd(response1.getId())
+                .exec();
+        /* create table container */
+        final String bind = new File("./src/test/resources/weather").toPath().toAbsolutePath() + ":/docker-entrypoint-initdb.d";
+        log.trace("container bind {}", bind);
+        final CreateContainerResponse response2 = dockerClient.createContainerCmd(IMAGE_2_REPOSITORY + ":" + IMAGE_2_TAG)
+                .withHostConfig(hostConfig.withNetworkMode(DATABASE_NET))
+                .withName(CONTAINER_1_INTERNALNAME)
+                .withIpv4Address(CONTAINER_1_IP)
+                .withHostName(CONTAINER_1_INTERNALNAME)
+                .withEnv("MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=weather")
+                .withBinds(Bind.parse(bind))
+                .exec();
+        /* wait */
+        CONTAINER_1.setHash(response2.getId());
+        Thread.sleep(10 * 1000);
+    }
+
+    @AfterAll
+    public static void afterAll() {
+        /* stop containers and remove them */
+        dockerClient.listContainersCmd()
+                .withShowAll(true)
+                .exec()
+                .forEach(container -> {
+                    log.info("Delete container {}", Arrays.asList(container.getNames()));
+                    try {
+                        dockerClient.stopContainerCmd(container.getId()).exec();
+                    } catch (NotModifiedException e) {
+                        // ignore
+                    }
+                    dockerClient.removeContainerCmd(container.getId()).exec();
+                });
+        /* remove networks */
+        dockerClient.listNetworksCmd()
+                .exec()
+                .stream()
+                .filter(n -> n.getName().startsWith("fda"))
+                .forEach(network -> {
+                    log.info("Delete network {}", network.getName());
+                    dockerClient.removeNetworkCmd(network.getId()).exec();
+                });
+    }
+
+    @BeforeEach
+    @Transactional
+    public void beforeEach() {
+        imageRepository.save(IMAGE_1);
+        TABLE_1.setDatabase(DATABASE_1);
+        tableRepository.save(TABLE_1);
+    }
+
+    @Test
+    public void insertFromTuple_succeeds() throws TableNotFoundException, TableMalformedException,
+            DatabaseNotFoundException, ImageNotSupportedException, InterruptedException, SQLException {
+        final TableCsvDto request = TableCsvDto.builder()
+                .data(List.of(new LinkedHashMap<>() {{
+                    put(COLUMN_1_1_NAME, 1L);
+                    put(COLUMN_1_2_NAME, "2020-12-01");
+                    put(COLUMN_1_3_NAME, "Somewhere");
+                    put(COLUMN_1_4_NAME, 15.0);
+                    put(COLUMN_1_5_NAME, 20.0);
+                }}))
+                .build();
+
+        /*  mock */
+        DockerConfig.startContainer(CONTAINER_1);
+        MariaDbConfig.clearDatabase(TABLE_1);
+
+        /* test */
+        dataEndpoint.insertFromTuple(DATABASE_1_ID, TABLE_1_ID, request);
+        assertTrue(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, 1L));
+        assertFalse(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, 2L));
+    }
+
+    @Test
+    public void insertFromTuple_10k_succeeds() throws TableNotFoundException, TableMalformedException,
+            DatabaseNotFoundException, ImageNotSupportedException, InterruptedException, SQLException {
+        /* config */
+        final long limit = 10_000L;
+
+        /* mock */
+        final Random random = new Random();
+        final List<Map<String, Object>> data = new LinkedList<>();
+        for (long i = 0L; i <= limit; i++) {
+            final Long id = i;
+            data.add(new LinkedHashMap<>() {{
+                put(COLUMN_1_1_NAME, id);
+                put(COLUMN_1_2_NAME, "2020-01-01");
+                put(COLUMN_1_3_NAME, NanoIdUtils.randomNanoId());
+                put(COLUMN_1_4_NAME, random.nextDouble());
+                put(COLUMN_1_5_NAME, random.nextDouble());
+            }});
+        }
+        final TableCsvDto request = TableCsvDto.builder()
+                .data(data)
+                .build();
+        DockerConfig.startContainer(CONTAINER_1);
+        MariaDbConfig.clearDatabase(TABLE_1);
+
+        /* test */
+        final long start = System.currentTimeMillis();
+        dataEndpoint.insertFromTuple(DATABASE_1_ID, TABLE_1_ID, request);
+        final long end = System.currentTimeMillis();
+        log.info("Inserted {}k records in {} seconds", limit / 1000, (end - start) / 1000.0);
+        assertTrue(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, 1L), "id 1 missing");
+        assertTrue(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, 1_000L), "id 1k missing");
+        assertTrue(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, 10_000L), "id 10k missing");
+        assertTrue(MariaDbConfig.contains(TABLE_1, COLUMN_1_1_NAME, limit), "id 100k missing");
+    }
+
+}
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointIntegrationTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointRestIntegrationTest.java
similarity index 98%
rename from fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointIntegrationTest.java
rename to fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointRestIntegrationTest.java
index c4f126ee54..4fd54a3086 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointIntegrationTest.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/DataEndpointRestIntegrationTest.java
@@ -49,7 +49,7 @@ import static org.mockito.ArgumentMatchers.any;
 @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
 @SpringBootTest
 @ExtendWith(SpringExtension.class)
-public class DataEndpointIntegrationTest extends BaseUnitTest {
+public class DataEndpointRestIntegrationTest extends BaseUnitTest {
 
     @MockBean
     private Channel channel;
@@ -100,6 +100,7 @@ public class DataEndpointIntegrationTest extends BaseUnitTest {
     public void beforeEach() {
         imageRepository.save(IMAGE_1);
         databaseRepository.save(DATABASE_1);
+        TABLE_1.setDatabase(DATABASE_1);
         tableRepository.save(TABLE_1);
     }
 
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
index f2b59ec195..a98a1552d9 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
@@ -9,6 +9,7 @@ import at.tuwien.endpoints.TableEndpoint;
 import at.tuwien.exception.*;
 import at.tuwien.repository.jpa.DatabaseRepository;
 import at.tuwien.repository.jpa.TableRepository;
+import at.tuwien.service.MessageQueueService;
 import at.tuwien.service.impl.TableServiceImpl;
 import com.rabbitmq.client.Channel;
 import org.junit.jupiter.api.Disabled;
@@ -21,6 +22,7 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -48,6 +50,9 @@ public class TableEndpointUnitTest extends BaseUnitTest {
     @MockBean
     private DatabaseRepository databaseRepository;
 
+    @MockBean
+    private MessageQueueService messageQueueService;
+
     @Autowired
     private TableEndpoint tableEndpoint;
 
@@ -63,18 +68,22 @@ public class TableEndpointUnitTest extends BaseUnitTest {
     }
 
     @Test
-    @Disabled("only works in integration test")
     public void create_succeeds() throws DatabaseNotFoundException, ImageNotSupportedException,
-            TableNotFoundException, DataProcessingException, ArbitraryPrimaryKeysException, TableMalformedException, AmqpException {
+            TableNotFoundException, DataProcessingException, ArbitraryPrimaryKeysException, TableMalformedException, AmqpException, IOException {
         final TableCreateDto request = TableCreateDto.builder()
                 .name(TABLE_1_NAME)
                 .description(TABLE_1_DESCRIPTION)
                 .columns(COLUMNS_CSV01)
                 .build();
+
+        /* mock */
         when(tableRepository.findById(TABLE_1_ID))
                 .thenReturn(Optional.of(TABLE_1));
         when(tableService.findById(DATABASE_1_ID, TABLE_1_ID))
                 .thenReturn(TABLE_1);
+        doNothing()
+                .when(messageQueueService)
+                .create(TABLE_1);
 
         /* test */
         final ResponseEntity<TableBriefDto> response = tableEndpoint.create(DATABASE_1_ID, request);
diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/service/DataServiceIntegrationTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/service/DataServiceIntegrationTest.java
index f2d80aa71e..04241bf9cb 100644
--- a/fda-table-service/rest-service/src/test/java/at/tuwien/service/DataServiceIntegrationTest.java
+++ b/fda-table-service/rest-service/src/test/java/at/tuwien/service/DataServiceIntegrationTest.java
@@ -67,9 +67,9 @@ public class DataServiceIntegrationTest extends BaseUnitTest {
     private MariaDataService dataService;
 
     @BeforeAll
-    public static void beforeAll() throws InterruptedException {
+    public static void beforeAll() {
         afterAll();
-        /* create network */
+        /* create networks */
         dockerClient.createNetworkCmd()
                 .withName("fda-userdb")
                 .withIpam(new Network.Ipam()
@@ -77,7 +77,14 @@ public class DataServiceIntegrationTest extends BaseUnitTest {
                                 .withSubnet("172.28.0.0/16")))
                 .withEnableIpv6(false)
                 .exec();
-        /* create container */
+        dockerClient.createNetworkCmd()
+                .withName("fda-public")
+                .withIpam(new Network.Ipam()
+                        .withConfig(new Network.Ipam.Config()
+                                .withSubnet("172.29.0.0/16")))
+                .withEnableIpv6(false)
+                .exec();
+        /* create weather container */
         final String bind = new File("./src/test/resources/weather").toPath().toAbsolutePath() + ":/docker-entrypoint-initdb.d";
         log.trace("container bind {}", bind);
         final CreateContainerResponse request = dockerClient.createContainerCmd(IMAGE_2_REPOSITORY + ":" + IMAGE_2_TAG)
@@ -88,19 +95,19 @@ public class DataServiceIntegrationTest extends BaseUnitTest {
                 .withEnv("MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=weather")
                 .withBinds(Bind.parse(bind))
                 .exec();
-        final String bind3 = new File("./src/test/resources/species").toPath().toAbsolutePath() + ":/docker-entrypoint-initdb.d";
-        log.trace("container 3 bind {}", bind3);
-        final CreateContainerResponse request3 = dockerClient.createContainerCmd(IMAGE_2_REPOSITORY + ":" + IMAGE_2_TAG)
-                .withHostConfig(hostConfig.withNetworkMode("fda-userdb"))
-                .withName(CONTAINER_3_INTERNALNAME)
-                .withIpv4Address(CONTAINER_3_IP)
-                .withHostName(CONTAINER_3_INTERNALNAME)
-                .withEnv("MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=biomedical")
-                .withBinds(Bind.parse(bind3))
+        /* create file container */
+        final String bind2 = new File("./src/test/resources/csv").toPath().toAbsolutePath() + ":/usr/share/nginx/html:ro";
+        log.trace("container bind2 {}", bind2);
+        final CreateContainerResponse request2 = dockerClient.createContainerCmd(CONTAINER_NGINX_IMAGE + ":" + CONTAINER_NGINX_TAG)
+                .withHostConfig(hostConfig.withNetworkMode(CONTAINER_NGINX_NET))
+                .withName(CONTAINER_NGINX_NAME)
+                .withIpv4Address(CONTAINER_NGINX_IP)
+                .withHostName(CONTAINER_NGINX_INTERNALNAME)
+                .withBinds(Bind.parse(bind2))
                 .exec();
         /* set hash */
         CONTAINER_1.setHash(request.getId());
-        CONTAINER_3.setHash(request3.getId());
+        CONTAINER_NGINX.setHash(request2.getId());
     }
 
     @AfterAll
@@ -272,15 +279,17 @@ public class DataServiceIntegrationTest extends BaseUnitTest {
             SQLException {
         final TableInsertDto request = TableInsertDto.builder()
                 .skipHeader(true)
-                .csvLocation("https://sandbox.zenodo.org/api/files/6aca3421-add3-489b-8c4a-35228fe5c683/species_id.csv")
+                .nullElement("NA")
+                .csvLocation("http://172.29.0.3/csv_01.csv")
                 .build();
 
         /* mock */
-        DockerConfig.startContainer(CONTAINER_3);
-        MariaDbConfig.clearDatabase(TABLE_3);
+        DockerConfig.startContainer(CONTAINER_1);
+        DockerConfig.startContainer(CONTAINER_NGINX);
+        MariaDbConfig.clearDatabase(TABLE_1);
 
         /* test */
-        dataService.insertCsv(DATABASE_3_ID, TABLE_3_ID, request);
+        dataService.insertCsv(DATABASE_1_ID, TABLE_1_ID, request);
     }
 
 }
diff --git a/fda-table-service/rest-service/src/test/resources/application.properties b/fda-table-service/rest-service/src/test/resources/application.properties
index 05fbac4c44..29b0c3c7ab 100644
--- a/fda-table-service/rest-service/src/test/resources/application.properties
+++ b/fda-table-service/rest-service/src/test/resources/application.properties
@@ -17,6 +17,7 @@ spring.jpa.hibernate.ddl-auto=create-drop
 spring.jpa.show-sql=false
 
 # logging
+logging.level.at.tuwien.service.impl.JdbcConnector=debug
 logging.level.at.tuwien.=trace
 
 # disable elasticsearch
diff --git a/fda-table-service/rest-service/src/test/resources/species/Species.sql b/fda-table-service/rest-service/src/test/resources/species/Species.sql
index aaa14c1ad8..957368fcc7 100644
--- a/fda-table-service/rest-service/src/test/resources/species/Species.sql
+++ b/fda-table-service/rest-service/src/test/resources/species/Species.sql
@@ -1,7 +1,7 @@
 /* https://sandbox.zenodo.org/api/files/6aca3421-add3-489b-8c4a-35228fe5c683/species_id.csv */
 CREATE TABLE maldi_ms_data
 (
-    mdb_qu      VARCHAR(255) NOT NULL,
-    mdb_species VARCHAR(255) NOT NULL,
-    mdb_score   VARCHAR(255) NOT NULL
+    qu      VARCHAR(255) NOT NULL,
+    species VARCHAR(255) NOT NULL,
+    score   VARCHAR(255) NOT NULL
 ) WITH SYSTEM VERSIONING;
\ No newline at end of file
diff --git a/fda-table-service/rest-service/src/test/resources/weather/WeatherAus.sql b/fda-table-service/rest-service/src/test/resources/weather/WeatherAus.sql
index 2b78c2210c..b53010acbb 100644
--- a/fda-table-service/rest-service/src/test/resources/weather/WeatherAus.sql
+++ b/fda-table-service/rest-service/src/test/resources/weather/WeatherAus.sql
@@ -1,14 +1,14 @@
 /* https://www.kaggle.com/jsphyg/weather-dataset-rattle-package */
 CREATE TABLE weather_aus
 (
-    mdb_id       BIGINT           NOT NULL PRIMARY KEY,
-    mdb_date     DATE             NOT NULL,
-    mdb_mintemp  DOUBLE PRECISION NULL,
-    mdb_location VARCHAR(255)     NULL,
-    mdb_rainfall DOUBLE PRECISION NULL
+    id       BIGINT           NOT NULL PRIMARY KEY,
+    date     DATE             NOT NULL,
+    location VARCHAR(255)     NULL,
+    mintemp  DOUBLE PRECISION NULL,
+    rainfall DOUBLE PRECISION NULL
 ) WITH SYSTEM VERSIONING;
 
-INSERT INTO weather_aus (mdb_id, mdb_date, mdb_location, mdb_mintemp, mdb_rainfall)
-VALUES (1, '2008-12-01'::DATE, 13.4, 'Albury', 0.6),
-       (2, '2008-12-02'::DATE, 7.4, 'Albury', 0),
-       (3, '2008-12-03'::DATE, 12.9, 'Albury', 0);
\ No newline at end of file
+INSERT INTO weather_aus (id, date, location, mintemp, rainfall)
+VALUES (1, '2008-12-01'::DATE, 'Albury', 13.4,  0.6),
+       (2, '2008-12-02'::DATE, 'Albury', 7.4, 0),
+       (3, '2008-12-03'::DATE, 'Albury', 12.9, 0);
\ No newline at end of file
diff --git a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
index c9b789f4e9..08ad55f934 100644
--- a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
+++ b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
@@ -139,7 +139,9 @@ public interface TableMapper {
 
     /* create sequence nonetheless, if it is used or not */
     default CreateSequenceFlagsStep tableCreateDtoToCreateSequenceFlagsStep(DSLContext context, TableCreateDto data) {
-        return context.createSequenceIfNotExists(tableNameToSequenceName(data.getName()));
+        final String sequenceName = tableNameToSequenceName(data.getName());
+        log.trace("create sequence {}", sequenceName);
+        return context.createSequenceIfNotExists(sequenceName);
     }
 
     default CreateTableColumnStep tableCreateDtoToCreateTableColumnStep(DSLContext context, TableCreateDto data)
@@ -238,7 +240,7 @@ public interface TableMapper {
     default List<Field<?>> tableToFieldList(Table data) {
         return data.getColumns()
                 .stream()
-                .sorted(Comparator.comparing(TableColumn::getOrdinalPosition))
+                .sorted()
                 .map(c -> field(c.getInternalName()))
                 .collect(Collectors.toList());
     }
@@ -250,15 +252,6 @@ public interface TableMapper {
                 .collect(Collectors.toList());
     }
 
-    default ForcedType columnCreateDtoToForcedType(TableCreateDto data, ColumnCreateDto column) {
-        final String name = columnCreateDtoToEnumTypeName(data, column);
-        final ForcedType type = new ForcedType()
-                .withName(name)
-                .withTypes("varchar")
-                .withExpression(".*" + name + ".*");
-        return type;
-    }
-
     default DataType<?> columnTypeDtoToDataType(TableCreateDto table, ColumnCreateDto data) {
         if (data.getPrimaryKey()) {
             if (data.getType().equals(ColumnTypeDto.NUMBER)) {
diff --git a/fda-table-service/services/src/main/java/at/tuwien/service/impl/JdbcConnector.java b/fda-table-service/services/src/main/java/at/tuwien/service/impl/JdbcConnector.java
index 81193a9f36..4ed1df95c2 100644
--- a/fda-table-service/services/src/main/java/at/tuwien/service/impl/JdbcConnector.java
+++ b/fda-table-service/services/src/main/java/at/tuwien/service/impl/JdbcConnector.java
@@ -13,26 +13,19 @@ import at.tuwien.mapper.ImageMapper;
 import at.tuwien.mapper.TableMapper;
 import at.tuwien.service.DatabaseConnector;
 import lombok.extern.log4j.Log4j2;
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.jooq.*;
 import org.jooq.Record;
 import org.jooq.exception.DataAccessException;
 import org.jooq.impl.DSL;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.web.servlet.server.Encoding;
 import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ResourceUtils;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
-import java.net.URI;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
@@ -76,7 +69,6 @@ public abstract class JdbcConnector implements DatabaseConnector {
         final CreateSequenceFlagsStep createSequenceFlagsStep = tableMapper.tableCreateDtoToCreateSequenceFlagsStep(context,
                 createDto);
         createSequenceFlagsStep.execute();
-        log.debug("created id sequence");
         final CreateTableColumnStep createTableColumnStep = tableMapper.tableCreateDtoToCreateTableColumnStep(context,
                 createDto);
         log.trace("before execution: {} ", createTableColumnStep.getSQL());
@@ -112,15 +104,13 @@ public abstract class JdbcConnector implements DatabaseConnector {
             log.warn("No data provided.");
             throw new TableMalformedException("No data provided");
         }
-        log.info("First row {}", data.getData().get(0));
-        log.info("Table columns {}", table.getColumns());
-        if (data.getData().get(0).size() != table.getColumns().size()) {
-            log.error("Provided columns differ from table columns found in metadata db.");
-            throw new TableMalformedException("Provided columns differ from table columns found in metadata db.");
-        }
         final List<Field<?>> headers = tableMapper.tableToFieldList(table);
-        log.trace("headers received {}", headers.stream().map(Field::getName).collect(Collectors.toList()));
-        log.trace("first row received {}", data.getData().size() > 0 ? data.getData().get(0) : null);
+        log.trace("first row {}", data.getData()
+                .get(0));
+        log.trace("table columns {}", table.getColumns()
+                .stream()
+                .map(TableColumn::getInternalName)
+                .collect(Collectors.toList()));
         final DSLContext context = open(table.getDatabase());
         final List<InsertValuesStepN<Record>> statements = new LinkedList<>();
         final Optional<TableColumn> idxColumn = table.getColumns()
@@ -136,15 +126,19 @@ public abstract class JdbcConnector implements DatabaseConnector {
                 log.trace("set auto-generated sequence value {}", idVal);
                 row.set(idx, idVal);
             }
-            statements.add(context.insertInto(table(table.getInternalName()), headers)
-                    .values(row));
+            try {
+                statements.add(context.insertInto(table(table.getInternalName()), headers)
+                        .values(row));
+            } catch (IllegalArgumentException e) {
+                throw new TableMalformedException("metadata field number differs from actual data field number", e);
+            }
         }
         try {
             log.trace("insertCsv statements {}", statements);
             context.batch(statements)
                     .execute();
         } catch (DataAccessException e) {
-            log.error("DataAccessException {}", e);
+            log.error("data access failed {}", e.getMessage());
             throw new TableMalformedException("Columns seem to differ or other problem with jOOQ mapper, most commonly it is a data type issue try with type 'STRING'", e);
         }
     }
diff --git a/fda-table-service/services/src/main/java/at/tuwien/service/impl/MariaDataService.java b/fda-table-service/services/src/main/java/at/tuwien/service/impl/MariaDataService.java
index 1becaf0a13..196af30707 100644
--- a/fda-table-service/services/src/main/java/at/tuwien/service/impl/MariaDataService.java
+++ b/fda-table-service/services/src/main/java/at/tuwien/service/impl/MariaDataService.java
@@ -123,7 +123,7 @@ public class MariaDataService extends JdbcConnector implements DataService {
     }
 
     protected TableCsvDto readCsv(Table table, TableInsertDto data) throws IOException, CsvException,
-            ArrayIndexOutOfBoundsException, TableMalformedException, FileStorageException, SQLException, ImageNotSupportedException {
+            ArrayIndexOutOfBoundsException, TableMalformedException, SQLException {
         log.trace("insert into table {} with params {}", table, data);
         if (data.getDelimiter() == null) {
             log.warn("No delimiter provided, using comma ','");
@@ -171,7 +171,12 @@ public class MariaDataService extends JdbcConnector implements DataService {
                 .build();
         final List<List<String>> rows = new LinkedList<>();
         reader.readAll()
-                .forEach(x -> rows.add(new ArrayList<>(List.of(x))));
+                .forEach(x -> {
+                    final ArrayList<String> row = new ArrayList<>();
+                    Collections.addAll(row, x);
+                    log.trace("add row {}", row);
+                    rows.add(row);
+                });
         log.trace("csv rows {}", rows.size());
         /* generic header, ref issue #95 */
         List<String> headers = TableUtils.fill(0, rows.get(0).size());
diff --git a/fda-table-service/services/src/main/java/at/tuwien/service/impl/RabbitMqService.java b/fda-table-service/services/src/main/java/at/tuwien/service/impl/RabbitMqService.java
index 5d35123746..fc1f75c3e8 100644
--- a/fda-table-service/services/src/main/java/at/tuwien/service/impl/RabbitMqService.java
+++ b/fda-table-service/services/src/main/java/at/tuwien/service/impl/RabbitMqService.java
@@ -84,6 +84,7 @@ public class RabbitMqService implements MessageQueueService {
         log.debug("declare fanout exchange {}", database.getExchange());
         channel.exchangeBind(database.getExchange(), AMQP_EXCHANGE, database.getExchange());
         log.debug("bind exchange {} to {}", database.getExchange(), AMQP_EXCHANGE);
+        log.info("Declared database exchange {} and bound to root exchange {}", database.getExchange(), AMQP_EXCHANGE);
     }
 
     @Override
@@ -93,6 +94,7 @@ public class RabbitMqService implements MessageQueueService {
         log.debug("declare queue {}", table.getTopic());
         channel.queueBind(table.getTopic(), table.getDatabase().getExchange(), table.getTopic());
         log.debug("bind queue {} to {}", table.getTopic(), table.getDatabase().getExchange());
+        log.info("Declared queue {} and bound to database exchange {}", table.getTopic(), table.getDatabase().getExchange());
     }
 
     @Override
-- 
GitLab