Kaynağa Gözat

Added JUnits for DB Infrastructure, Observations, Locations

Lukas Cerny 1 yıl önce
ebeveyn
işleme
782a08db09

+ 4 - 4
src/main/java/cz/senslog/telemetry/database/domain/SensorTelemetry.java

@@ -5,16 +5,16 @@ import java.time.OffsetDateTime;
 public class SensorTelemetry {
 
     private final long id;
-    private final double value;
+    private final Object value;
     private final OffsetDateTime timestamp;
     private final Location location;
     private final int speed;
 
-    public static SensorTelemetry of(long id, double value, OffsetDateTime timestamp, Location location, int speed) {
+    public static SensorTelemetry of(long id, Object value, OffsetDateTime timestamp, Location location, int speed) {
         return new SensorTelemetry(id, value, timestamp, location, speed);
     }
 
-    public SensorTelemetry(long id, double value, OffsetDateTime timestamp, Location location, int speed) {
+    public SensorTelemetry(long id, Object value, OffsetDateTime timestamp, Location location, int speed) {
         this.id = id;
         this.value = value;
         this.timestamp = timestamp;
@@ -38,7 +38,7 @@ public class SensorTelemetry {
         return speed;
     }
 
-    public double getValue() {
+    public Object getValue() {
         return value;
     }
 }

+ 169 - 72
src/main/java/cz/senslog/telemetry/database/repository/MapLogRepository.java

@@ -5,11 +5,13 @@ import cz.senslog.telemetry.database.SortType;
 import cz.senslog.telemetry.database.domain.*;
 import cz.senslog.telemetry.database.domain.Filter;
 import io.vertx.core.Future;
+import io.vertx.core.json.JsonArray;
 import io.vertx.core.json.JsonObject;
 import io.vertx.sqlclient.*;
 
 import java.time.OffsetDateTime;
 import java.util.*;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
@@ -30,6 +32,9 @@ public class MapLogRepository implements SensLogRepository {
         return new MapLogRepository(client);
     }
 
+    public Pool rawPool() {
+        return this.client;
+    }
 
     private static JsonObject convertSensorIdToName(Map<Long, String> sensors, JsonObject observedValues) {
         JsonObject res = new JsonObject(new HashMap<>(observedValues.size()));
@@ -187,8 +192,8 @@ public class MapLogRepository implements SensLogRepository {
                 .map(rs -> StreamSupport.stream(rs.spliterator(), false)
                         .map((row) -> Unit.of(
                                 row.getLong("unit_id"),
-                                row.getString("imei"),
                                 row.getString("name"),
+                                row.getString("imei"),
                                 row.getString("description")
                         ))
                         .collect(toList())
@@ -224,7 +229,7 @@ public class MapLogRepository implements SensLogRepository {
 
     @Override
     public Future<Sensor> findSensorById(long sensorId) {
-        return client.preparedQuery("SELECT s.sensor_id, s.name, s.type, s.io_id, s.description, p.id AS phenomenon_id, p.name AS phenomenon_name, p.uom, p.uom_link " +
+        return client.preparedQuery("SELECT s.sensor_id, s.name, s.type, s.io_id, s.description, p.id AS phenomenon_id, p.name AS phenomenon_name " +
                         "FROM maplog.sensor AS s " +
                         "JOIN maplog.phenomenon p on p.id = s.phenomenon_id " +
                         "WHERE s.sensor_id = $1")
@@ -421,7 +426,7 @@ public class MapLogRepository implements SensLogRepository {
 
     @Override
     public Future<Sensor> findSensorByCampaignIdAndUnitId(long campaignId, long unitId, long sensorId) {
-        return client.preparedQuery("SELECT s.sensor_id, s.name, s.type, s.io_id, s.description, p.id AS phenomenon_id, p.name AS phenomenon_name, p.uom, p.uom_link " +
+        return client.preparedQuery("SELECT s.sensor_id, s.name, s.type, s.io_id, s.description, p.id AS phenomenon_id, p.name AS phenomenon_name " +
                         "FROM maplog.sensor AS s " +
                         "JOIN maplog.phenomenon AS p ON p.id = s.phenomenon_id " +
                         "JOIN maplog.unit_to_sensor AS uts ON s.sensor_id = uts.sensor_id " +
@@ -1290,16 +1295,16 @@ public class MapLogRepository implements SensLogRepository {
         String whereTimestampClause;
         Tuple tupleParams;
         if (from != null && to != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $4 < c.from_time THEN c.from_time ELSE $4 END) AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN $5 ELSE (CASE WHEN $5 > c.to_time THEN c.to_time ELSE $5 END) END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $4 < utc.from_time THEN utc.from_time ELSE $4 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $5 ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
             tupleParams = Tuple.of(campaignId, offset, limit, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $4 < c.from_time THEN c.from_time ELSE $4 END) AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN now() ELSE c.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $4 < utc.from_time THEN utc.from_time ELSE $4 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, offset, limit, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= c.from_time AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN $4 ELSE (CASE WHEN $4 > c.to_time THEN c.to_time ELSE $4 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $4 ELSE (CASE WHEN $4 > utc.to_time THEN utc.to_time ELSE $4 END) END)";
             tupleParams = Tuple.of(campaignId, offset, limit, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= c.from_time AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN now() ELSE c.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, offset, limit);
         }
 
@@ -1319,7 +1324,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -1333,7 +1343,6 @@ public class MapLogRepository implements SensLogRepository {
                         "ST_M (tel.the_geom) AS angle " +
                     "FROM maplog.obs_telemetry AS tel " +
                     "JOIN maplog.unit_to_campaign AS utc ON tel.unit_id = utc.unit_id " +
-                    "JOIN maplog.campaign AS c ON c.id = utc.campaign_id " +
                     "WHERE utc.campaign_id = $1 AND " + whereTimestampClause + whereFiltersClause + " " +
                     "ORDER BY tel.time_stamp OFFSET $2 LIMIT $3;";
 
@@ -1370,16 +1379,16 @@ public class MapLogRepository implements SensLogRepository {
         String whereTimestampClause;
         Tuple tupleParams;
         if (from != null && to != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < c.from_time THEN c.from_time ELSE $5 END) AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > c.to_time THEN c.to_time ELSE $6 END) END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, offset, limit, userIdentity, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < c.from_time THEN c.from_time ELSE $5 END) AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN now() ELSE c.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, offset, limit, userIdentity, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= c.from_time AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN $5 ELSE (CASE WHEN $5 > c.to_time THEN c.to_time ELSE $5 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $5 ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
             tupleParams = Tuple.of(campaignId, offset, limit, userIdentity, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= c.from_time AND tel.time_stamp <= (CASE WHEN c.to_time IS NULL THEN now() ELSE c.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, offset, limit, userIdentity);
         }
 
@@ -1399,7 +1408,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -1416,7 +1430,6 @@ public class MapLogRepository implements SensLogRepository {
                     "JOIN maplog.unit_to_campaign AS utc ON tel.unit_id = utc.unit_id " +
                     "JOIN maplog.user_to_campaign_config AS uc ON uc.campaign_id = utc.campaign_id and uc.entity_id = e.entity_id " +
                     "JOIN maplog.system_user AS su ON su.id = uc.user_id " +
-                    "JOIN maplog.campaign AS c ON c.id = utc.campaign_id " +
                     "WHERE utc.campaign_id = $1 AND su.identity = $4 AND " + whereTimestampClause + whereFiltersClause + " " +
                     "ORDER BY tel.time_stamp OFFSET $2 LIMIT $3;";
 
@@ -1454,16 +1467,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $5 ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit);
         }
 
@@ -1483,7 +1496,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -1533,16 +1551,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $7 ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity);
         }
 
@@ -1562,7 +1580,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -1776,16 +1799,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $7 ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit);
         }
 
@@ -1805,14 +1828,35 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
             }
         }
 
-        String sql = "SELECT tel.id, (tel.observed_values::jsonb ->> $3::bigint::text::varchar)::double precision AS value, tel.speed, tel.time_stamp, " +
+        final BiFunction<String, Row, Object> castSensorValue = (type, row) -> {
+              switch (type) {
+                  case "number" -> {
+                      return Double.valueOf(row.getString("value"));
+                  }
+                  case "array"  -> {
+                      return new JsonArray(row.getString("value"));
+                  }
+                  default -> {
+                      throw new IllegalArgumentException(String.format("Unsupported type <%s> of sensor's value.", type));
+                  }
+              }
+        };
+
+        String sql = "SELECT tel.id, tel.speed, tel.time_stamp, " +
+                        "tel.observed_values::jsonb ->> $3::bigint::text::varchar AS value, " +
+                        "jsonb_typeof((tel.observed_values::jsonb ->> $3::bigint::text::varchar)::jsonb) AS value_type, " +
                         "ST_X (ST_Transform (tel.the_geom, 4326)) AS long, " +
                         "ST_Y (ST_Transform (tel.the_geom, 4326)) AS lat, " +
                         "ST_Z (ST_Transform (tel.the_geom, 4326)) AS alt, " +
@@ -1828,7 +1872,7 @@ public class MapLogRepository implements SensLogRepository {
                 .map(rs -> StreamSupport.stream(rs.spliterator(), false)
                         .map(r -> SensorTelemetry.of(
                                 r.getLong("id"),
-                                r.getDouble("value"),
+                                castSensorValue.apply(r.getString("value_type"), r),
                                 r.getOffsetDateTime("time_stamp"),
                                 Location.of(
                                         r.getFloat("long"),
@@ -1848,16 +1892,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $7 < utc.from_time THEN utc.from_time ELSE $7 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $8 > utc.to_time THEN utc.to_time ELSE $8 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $8 ELSE (CASE WHEN $8 > utc.to_time THEN utc.to_time ELSE $8 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, userIdentity, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $7 < utc.from_time THEN utc.from_time ELSE $7 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $7 < utc.from_time THEN utc.from_time ELSE $7 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, userIdentity, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $7 ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, userIdentity, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, sensorId, offset, limit, userIdentity);
         }
 
@@ -1877,14 +1921,35 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
             }
         }
 
-        String sql = "SELECT tel.id, (tel.observed_values::jsonb ->> $3::bigint::text::varchar)::double precision AS value, tel.speed, tel.time_stamp, " +
+        final BiFunction<String, Row, Object> castSensorValue = (type, row) -> {
+            switch (type) {
+                case "number" -> {
+                    return Double.valueOf(row.getString("value"));
+                }
+                case "array"  -> {
+                    return new JsonArray(row.getString("value"));
+                }
+                default -> {
+                    throw new IllegalArgumentException(String.format("Unsupported type <%s> of sensor's value.", type));
+                }
+            }
+        };
+
+        String sql = "SELECT tel.id, tel.speed, tel.time_stamp, " +
+                        "tel.observed_values::jsonb ->> $3::bigint::text::varchar AS value, " +
+                        "jsonb_typeof((tel.observed_values::jsonb ->> $3::bigint::text::varchar)::jsonb) AS value_type, " +
                         "ST_X (ST_Transform (tel.the_geom, 4326)) AS long, " +
                         "ST_Y (ST_Transform (tel.the_geom, 4326)) AS lat, " +
                         "ST_Z (ST_Transform (tel.the_geom, 4326)) AS alt, " +
@@ -1904,7 +1969,7 @@ public class MapLogRepository implements SensLogRepository {
                 .map(rs -> StreamSupport.stream(rs.spliterator(), false)
                         .map(r -> SensorTelemetry.of(
                                 r.getLong("id"),
-                                r.getDouble("value"),
+                                castSensorValue.apply(r.getString("value_type"), r),
                                 r.getOffsetDateTime("time_stamp"),
                                 Location.of(
                                         r.getFloat("long"),
@@ -1924,16 +1989,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $5 < utc.from_time THEN utc.from_time ELSE $5 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $5 > utc.to_time THEN utc.to_time ELSE $5 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit);
         }
 
@@ -1953,7 +2018,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -1994,16 +2064,16 @@ public class MapLogRepository implements SensLogRepository {
         Tuple tupleParams;
         if (from != null && to != null) {
             whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) " +
-                    "AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
+                    "AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $7 ELSE (CASE WHEN $7 > utc.to_time THEN utc.to_time ELSE $7 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, from, to);
         } else if (from != null) {
-            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= (CASE WHEN $6 < utc.from_time THEN utc.from_time ELSE $6 END) AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, from);
         } else if (to != null) {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN $6 ELSE (CASE WHEN $6 > utc.to_time THEN utc.to_time ELSE $6 END) END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity, to);
         } else {
-            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp <= (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
+            whereTimestampClause = "tel.time_stamp >= utc.from_time AND tel.time_stamp < (CASE WHEN utc.to_time IS NULL THEN now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, unitId, offset, limit, userIdentity);
         }
 
@@ -2023,7 +2093,12 @@ public class MapLogRepository implements SensLogRepository {
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (tel.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -2201,19 +2276,23 @@ public class MapLogRepository implements SensLogRepository {
     public Future<List<UnitLocation>> findUnitsLocationsByCampaignId(
             long campaignId, int limitPerUnit, OffsetDateTime from, OffsetDateTime to, SortType sort, List<Filter> filters
     ) {
+        if (limitPerUnit <= 0) {
+            return Future.succeededFuture(Collections.emptyList());
+        }
+
         String whereTimestampClause;
         Tuple tupleParams;
         if (from != null && to != null) {
-            whereTimestampClause = "WHERE utc.campaign_id = $1 AND time_stamp >= $3 AND time_stamp < $4";
+            whereTimestampClause = "utc.campaign_id = $1 AND obs.time_stamp >= (CASE WHEN $3 < utc.from_time THEN utc.from_time ELSE $3 END) AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  $4 ELSE (CASE WHEN utc.to_time > $4 THEN $4 ELSE utc.to_time END) END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, from, to);
         } else if (from != null) {
-            whereTimestampClause = "WHERE utc.campaign_id = $1 AND time_stamp >= $3";
+            whereTimestampClause = "utc.campaign_id = $1 AND obs.time_stamp >= (CASE WHEN $3 < utc.from_time THEN utc.from_time ELSE $3 END) AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, from);
         } else if (to != null) {
-            whereTimestampClause = "WHERE utc.campaign_id = $1 AND time_stamp < $3";
+            whereTimestampClause = "utc.campaign_id = $1 AND obs.time_stamp >= utc.from_time AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  $3 ELSE (CASE WHEN utc.to_time > $3 THEN $3 ELSE utc.to_time END) END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, to);
         } else {
-            whereTimestampClause = "WHERE utc.campaign_id = $1";
+            whereTimestampClause = "utc.campaign_id = $1 AND obs.time_stamp >= utc.from_time AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit);
         }
 
@@ -2224,16 +2303,21 @@ public class MapLogRepository implements SensLogRepository {
             switch (filter.getType()) {
                 case UNIT -> {
                     switch (filter.getAttribute()) {
-                        case SPEED       -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND speed %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case LONGITUDE   -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_X (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case LATITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Y (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case ALTITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Z (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case SPEED       -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND obs.speed %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case LONGITUDE   -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_X (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case LATITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Y (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case ALTITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Z (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
                     }
                 }
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((obs.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (obs.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -2246,8 +2330,9 @@ public class MapLogRepository implements SensLogRepository {
                     "ST_Z (ST_Transform (the_geom, 4326)) AS alt, " +
                     "ST_M (the_geom) AS angle " +
                 "FROM (SELECT *, row_number() OVER (PARTITION BY unit_id ORDER BY time_stamp "+ sort.name() +" ) AS rn " +
-                "FROM (SELECT obs.* FROM maplog.obs_telemetry obs JOIN maplog.unit_to_campaign utc ON obs.unit_id = utc.unit_id "+whereTimestampClause+") AS data) AS g " +
-                "WHERE rn <= $2" + whereFiltersClause + " ORDER BY time_stamp";
+                "FROM (SELECT obs.* FROM maplog.obs_telemetry obs JOIN maplog.unit_to_campaign utc ON obs.unit_id = utc.unit_id " +
+                    "WHERE "+whereTimestampClause + whereFiltersClause + ") AS data) AS g " +
+                "WHERE rn <= $2 ORDER BY time_stamp";
 
         return client.preparedQuery(sql)
                 .execute(tupleParams)
@@ -2267,20 +2352,26 @@ public class MapLogRepository implements SensLogRepository {
     }
 
     @Override
-    public Future<List<UnitLocation>> findUnitsLocationsByIdentityAndCampaignId(String userIdentity, long campaignId, int limitPerUnit, OffsetDateTime from, OffsetDateTime to, SortType sort, List<Filter> filters) {
+    public Future<List<UnitLocation>> findUnitsLocationsByIdentityAndCampaignId(
+            String userIdentity, long campaignId, int limitPerUnit, OffsetDateTime from, OffsetDateTime to, SortType sort, List<Filter> filters
+    ) {
+        if (limitPerUnit <= 0) {
+            return Future.succeededFuture(Collections.emptyList());
+        }
+
         String whereTimestampClause;
         Tuple tupleParams;
         if (from != null && to != null) {
-            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND time_stamp >= $4 AND time_stamp < $5";
+            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND  obs.time_stamp >= (CASE WHEN $4 < utc.from_time THEN utc.from_time ELSE $4 END) AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  $5 ELSE (CASE WHEN utc.to_time > $4 THEN $5 ELSE utc.to_time END) END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, userIdentity, from, to);
         } else if (from != null) {
-            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND time_stamp >= $4";
+            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND obs.time_stamp >= (CASE WHEN $4 < utc.from_time THEN utc.from_time ELSE $4 END) AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, userIdentity, from);
         } else if (to != null) {
-            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND time_stamp < $4";
+            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND obs.time_stamp >= utc.from_time AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  $4 ELSE (CASE WHEN utc.to_time > $4 THEN $4 ELSE utc.to_time END) END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, userIdentity, to);
         } else {
-            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3";
+            whereTimestampClause = "utc.campaign_id = $1 AND su.identity = $3 AND obs.time_stamp >= utc.from_time AND obs.time_stamp < (CASE WHEN utc.to_time IS NULL THEN  now() ELSE utc.to_time END)";
             tupleParams = Tuple.of(campaignId, limitPerUnit, userIdentity);
         }
 
@@ -2291,16 +2382,21 @@ public class MapLogRepository implements SensLogRepository {
             switch (filter.getType()) {
                 case UNIT -> {
                     switch (filter.getAttribute()) {
-                        case SPEED       -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND speed %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case LONGITUDE   -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_X (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case LATITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Y (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
-                        case ALTITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Z (ST_Transform (the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case SPEED       -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND obs.speed %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case LONGITUDE   -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_X (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case LATITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Y (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
+                        case ALTITUDE    -> of(tupleParams.addFloat(optValue)).map(p -> format(" AND ST_Z (ST_Transform (obs.the_geom, 4326)) %s $%d", opt, p.size())).ifPresent(whereFiltersClause::append);
                     }
                 }
                 case SENSOR -> {
                     if (Objects.requireNonNull(filter.getAttribute()) == FilterAttribute.ID) {
                         of(tupleParams.addLong(filter.getAttributeValueAsLong()).addFloat(optValue))
-                                .map(p -> format(" AND (observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d", p.size() - 1, opt, p.size()))
+                                .map(p -> {
+                                    int attrInd = p.size() - 1;
+                                    int valueInd = p.size();
+                                    return format(" AND (CASE WHEN jsonb_typeof((obs.observed_values::jsonb ->> $%d::bigint::text::varchar)::jsonb) = 'number' " +
+                                            "THEN (CASE WHEN (obs.observed_values::jsonb ->> $%d::bigint::text::varchar)::double precision %s $%d THEN 1 ELSE 0 END) ELSE 0 END) = 1", attrInd, attrInd, opt, valueInd);
+                                })
                                 .ifPresent(whereFiltersClause::append);
                     }
                 }
@@ -2318,8 +2414,9 @@ public class MapLogRepository implements SensLogRepository {
                     "JOIN maplog.event e on e.unit_id = obs.unit_id " +
                     "JOIN maplog.unit_to_campaign utc ON obs.unit_id = utc.unit_id " +
                     "JOIN maplog.user_to_campaign_config uc ON uc.campaign_id = utc.campaign_id and uc.entity_id = e.entity_id " +
-                    "JOIN maplog.system_user su on su.id = uc.user_id WHERE "+whereTimestampClause+
-                ") AS data) AS g WHERE rn <= $2" + whereFiltersClause + " ORDER BY time_stamp";
+                    "JOIN maplog.system_user su on su.id = uc.user_id " +
+                "WHERE "+whereTimestampClause + whereFiltersClause + ") AS data) AS g " +
+                "WHERE rn <= $2 ORDER BY time_stamp";
 
         return client.preparedQuery(sql)
                 .execute(tupleParams)

+ 1 - 1
src/main/java/cz/senslog/telemetry/server/ws/OpenAPIHandler.java

@@ -87,7 +87,7 @@ public class OpenAPIHandler {
 
         CascadeCondition.<AuthBearerUser, Future<List<Campaign>>>ofNullable(user)
                 .ifThenAsDefault(u -> u.hasPermissionScope(READ_INFRASTRUCTURE), repo::allCampaigns)
-                .ifThen(u -> u.hasPermissionScope(READ_PERSONAL),        u -> repo.allCampaignsByIdentity(u.getId()))
+                .ifThen(u -> u.hasPermissionScope(READ_PERSONAL), u -> repo.allCampaignsByIdentity(u.getId()))
                 .execute(r -> r.onSuccess(data -> rc.response().end(new JsonArray(
                             data.stream().map(c -> (navigationLinks ? JsonObject.of(
                                     "Campaign@NavigationLink", String.format("%s/campaigns/%d", host, c.getId())

+ 7 - 1
src/main/java/cz/senslog/telemetry/server/ws/WSParameters.java

@@ -150,7 +150,13 @@ public class WSParameters {
 
         public int limitPerUnit() {
             String strParam = queryParams.get("limitPerUnit");
-            return backupAs("limitPerUnit", strParam != null ? Integer.parseInt(strParam) : DEFAULT_LIMIT_PER_UNIT);
+            if (strParam != null) {
+                int limitPerUnit = Integer.parseInt(strParam);
+                if (limitPerUnit > 0) {
+                    return backupAs("limitPerUnit", limitPerUnit);
+                }
+            }
+            return backupAs("limitPerUnit", DEFAULT_LIMIT_PER_UNIT);
         }
 
         public SortType sort() {

+ 0 - 1
src/main/resources/openAPISpec.yaml

@@ -1574,7 +1574,6 @@ components:
     limitPerUnitParam:
       in: query
       name: limitPerUnit
-      required: true
       schema:
         type: integer
         default: 1

+ 3739 - 5
src/test/java/cz/senslog/telemetry/database/repository/MapLogRepositoryTest.java

@@ -2,8 +2,12 @@ package cz.senslog.telemetry.database.repository;
 
 import cz.senslog.telemetry.TestResourceUtils;
 import cz.senslog.telemetry.app.PropertyConfig;
-import cz.senslog.telemetry.database.domain.Campaign;
+import cz.senslog.telemetry.database.DataNotFoundException;
+import cz.senslog.telemetry.database.SortType;
+import cz.senslog.telemetry.database.domain.*;
 import io.vertx.core.Vertx;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
 import io.vertx.junit5.VertxExtension;
 import io.vertx.junit5.VertxTestContext;
 import io.vertx.sqlclient.Pool;
@@ -20,11 +24,13 @@ import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import static java.time.ZoneOffset.UTC;
+import static java.util.Collections.emptyList;
+import static org.assertj.core.api.Assertions.*;
 
 @ExtendWith({VertxExtension.class, SystemStubsExtension.class})
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -34,6 +40,8 @@ class MapLogRepositoryTest {
     private static final String JUNIT_DB_SETUP = "db/junit_setup.sql";
     private static final String JUNIT_DB_TEARDOWN = "db/junit_teardown.sql";
 
+    private static final String USER_IDENTITY = "#identity";
+
     @SystemStub
     private EnvironmentVariables envVariable;
 
@@ -71,20 +79,3746 @@ class MapLogRepositoryTest {
     }
 
     @Test
+    @DisplayName("DB Test: allCampaigns#1")
     void allCampaigns(Vertx vertx, VertxTestContext testContext) {
         repo.allCampaigns().onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Campaign c1 = data.get(0);
+            assertThat(c1.getId()).isEqualTo(1);
+            assertThat(c1.getName()).isEqualTo("mock(name)");
+            assertThat(c1.getDescription()).isEqualTo("mock(description)");
+            assertThat(c1.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c1.getToTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2024, 3, 1, 0, 0, 0), UTC));
+
+            Campaign c2 = data.get(1);
+            assertThat(c2.getId()).isEqualTo(2);
+            assertThat(c2.getName()).isEqualTo("mock(name)");
+            assertThat(c2.getDescription()).isEqualTo("mock(description)");
+            assertThat(c2.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c2.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allCampaignsByIdentity#1")
+    void allCampaignsByIdentity_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allCampaignsByIdentity(USER_IDENTITY).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
             assertThat(data.size()).isEqualTo(1);
 
             Campaign c = data.get(0);
+            assertThat(c.getId()).isEqualTo(2);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignById#1")
+    void findCampaignById_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignById(1).onComplete(testContext.succeeding(c -> testContext.verify(() -> {
+
             assertThat(c.getId()).isEqualTo(1);
             assertThat(c.getName()).isEqualTo("mock(name)");
             assertThat(c.getDescription()).isEqualTo("mock(description)");
-            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 24, 18, 30, 0), ZoneOffset.UTC));
-            assertThat(c.getToTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2024, 12, 24, 18, 30, 0), ZoneOffset.UTC));
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c.getToTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2024, 3, 1, 0, 0, 0), UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignById#2")
+    void findCampaignById_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignById(2).onComplete(testContext.succeeding(c -> testContext.verify(() -> {
+
+            assertThat(c.getId()).isEqualTo(2);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignByIdentityAndId#1")
+    void findCampaignByIdentityAndId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignByIdentityAndId(USER_IDENTITY, 2).onComplete(testContext.succeeding(c -> testContext.verify(() -> {
+
+            assertThat(c.getId()).isEqualTo(2);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 23, 0, 0, 0), UTC));
+            assertThat(c.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignByIdentityAndId#2")
+    void findCampaignByIdentityAndId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignByIdentityAndId(USER_IDENTITY, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByCampaignId#1")
+    void findUnitsByCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByCampaignId(1).onComplete(testContext.succeeding((data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            CampaignUnit u1 = data.get(0);
+            assertThat(u1.getCampaignId()).isEqualTo(1);
+            assertThat(u1.getUnitId()).isEqualTo(1000);
+            assertThat(u1.getName()).isEqualTo("mock(name)");
+            assertThat(u1.getDescription()).isEqualTo("mock(description)");
+            assertThat(u1.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 24, 0, 0, 0), UTC));
+            assertThat(u1.getToTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2024, 3, 1, 0, 0, 0), UTC));
+
+            CampaignUnit u2 = data.get(1);
+            assertThat(u2.getCampaignId()).isEqualTo(1);
+            assertThat(u2.getUnitId()).isEqualTo(3000);
+            assertThat(u2.getName()).isEqualTo("mock(name)");
+            assertThat(u2.getDescription()).isEqualTo("mock(description)");
+            assertThat(u2.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 24, 0, 0, 0), UTC));
+            assertThat(u2.getToTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2024, 3, 1, 0, 0, 0), UTC));
+
+            testContext.completeNow();
+        }))));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByCampaignId#2")
+    void findUnitsByCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByCampaignId(2).onComplete(testContext.succeeding((data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            CampaignUnit u = data.get(0);
+            assertThat(u.getCampaignId()).isEqualTo(2);
+            assertThat(u.getUnitId()).isEqualTo(2000);
+            assertThat(u.getName()).isEqualTo("mock(name)");
+            assertThat(u.getDescription()).isEqualTo("mock(description)");
+            assertThat(u.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 24, 0, 0, 0), UTC));
+            assertThat(u.getToTime()).isNull();
+            testContext.completeNow();
+        }))));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByIdentityAndCampaignId#1")
+    void findUnitsByIdentityAndCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByIdentityAndCampaignId(USER_IDENTITY, 2).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            CampaignUnit u = data.get(0);
+            assertThat(u.getCampaignId()).isEqualTo(2);
+            assertThat(u.getUnitId()).isEqualTo(2000);
+            assertThat(u.getName()).isEqualTo("mock(name)");
+            assertThat(u.getDescription()).isEqualTo("mock(description)");
+            assertThat(u.getFromTime()).isEqualTo(OffsetDateTime.of(LocalDateTime.of(2023, 12, 24, 0, 0, 0), UTC));
+            assertThat(u.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByIdentityAndCampaignId#2")
+    void findUnitsByIdentityAndCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByIdentityAndCampaignId(USER_IDENTITY, 1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#1")
+    void findObservationsByCampaignIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdWithPaging(1, null, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t1.getSpeed()).isEqualTo(0);
+            assertThat(t1.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(1001)", 11.11,
+                    "sensor(1002)", 12.12
+            ));
+
+            UnitTelemetry t3 = data.data().get(3);
+            assertThat(t3.getUnitId()).isEqualTo(3000);
+            assertThat(t3.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t5 = data.data().get(4);
+            assertThat(t5.getUnitId()).isEqualTo(1000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+            assertThat(t5.getLocation()).isNotNull();
+            assertThat(t5.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t5.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t5.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t5.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t5.getSpeed()).isEqualTo(0);
+            assertThat(t5.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(1002)", 0d
+            ));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#2")
+    void findObservationsByCampaignIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#3")
+    void findObservationsByCampaignIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#4")
+    void findObservationsByCampaignIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#5")
+    void findObservationsByCampaignIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(3000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(1);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#6")
+    void findObservationsByCampaignIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(1000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#7")
+    void findObservationsByCampaignIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdWithPaging(1, null, null, 4, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#8")
+    void findObservationsByCampaignIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByCampaignIdWithPaging(1, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(10);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#9")
+    void findObservationsByCampaignIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByCampaignIdWithPaging(1, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#10")
+    void findObservationsByCampaignIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#11")
+    void findObservationsByCampaignIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        // No observations after the end of the campaign
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdWithPaging(1, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#12")
+    void findObservationsByCampaignIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findObservationsByCampaignIdWithPaging(10, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdWithPaging#13")
+    void findObservationsByCampaignIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1002", FilterOperation.EQ, 12)
+        );
+
+        repo.findObservationsByCampaignIdWithPaging(1, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#1")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t1.getSpeed()).isEqualTo(0);
+            assertThat(t1.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(2001)", 21.21,
+                    "sensor(2002)", 22.22
+            ));
+
+            UnitTelemetry t5 = data.data().get(4);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+            assertThat(t5.getLocation()).isNotNull();
+            assertThat(t5.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t5.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t5.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t5.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t5.getSpeed()).isEqualTo(0);
+            assertThat(t5.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(2002)", 0d
+            ));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#2")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#3")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#4")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
 
             testContext.completeNow();
         })));
     }
 
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#5")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(1);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#6")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#7")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, null, 3, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
 
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#8")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(10);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#9")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#10")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#11")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 10, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#12")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdWithPaging#13")
+    void findObservationsByIdentityAndCampaignIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2002", FilterOperation.EQ, 22)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdWithPaging(USER_IDENTITY, 2, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#1")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 4, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(4);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t1.getSpeed()).isEqualTo(0);
+            assertThat(t1.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(1001)", 11.11,
+                    "sensor(1002)",12.12
+            ));
+
+            UnitTelemetry t5 = data.data().get(3);
+            assertThat(t5.getUnitId()).isEqualTo(1000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+            assertThat(t5.getLocation()).isNotNull();
+            assertThat(t5.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t5.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t5.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t5.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t5.getSpeed()).isEqualTo(0);
+            assertThat(t5.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(1002)", 0d
+            ));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#2")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#3")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#4")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t2 = data.data().get(0);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#5")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(1000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#6")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 4, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#7")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(10);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#8")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#9")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#10")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations after the end of the campaign
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#11")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(10, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#12")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 2000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#13")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+        //  No access to the unit 1000 from the campaign 2
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(2, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#14")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 3000, null, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(3000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t1.getSpeed()).isEqualTo(0);
+            assertThat(t1.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(3001)", JsonArray.of(1, 2, 3, 4)
+            ));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdWithPaging#15")
+    void findObservationsByCampaignIdAndUnitIdWithPaging_NM15(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1002", FilterOperation.EQ, 12)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#1")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY, 2, 2000, null, null, 0, 4, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(4);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t1.getSpeed()).isEqualTo(0);
+            assertThat(t1.getObservedValues()).isEqualTo(JsonObject.of(
+                    "sensor(2001)", 21.21,
+                    "sensor(2002)", 22.22
+            ));
+
+            UnitTelemetry t5 = data.data().get(3);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+            assertThat(t5.getLocation()).isNotNull();
+            assertThat(t5.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t5.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t5.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t5.getLocation().getAngle()).isEqualTo(0f);
+            assertThat(t5.getSpeed()).isEqualTo(0);
+            assertThat(t5.getObservedValues()).isEqualTo(JsonObject.of());
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#2")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#3")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#4")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t2 = data.data().get(1);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#5")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitTelemetry t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#6")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 4, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#7")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(10);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#8")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#9")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#10")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations after the end of the campaign
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#11")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,10, 2000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#12")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+        // No access to the unit 1000 from the campaign 2
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#13")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+        // No user's access to the campaign 1
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY,1, 3000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging#14")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2002", FilterOperation.EQ, 22)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdWithPaging(USER_IDENTITY, 2, 2000, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitTelemetry t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+            assertThat(t.getSpeed()).isEqualTo(15);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#1")
+    void findUnitsLocationsByCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(l1.getLocation()).isNotNull();
+            assertThat(l1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(l1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(l1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(l1.getLocation().getAngle()).isEqualTo(0f);
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(3000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#2")
+    void findUnitsLocationsByCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.DESC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(3000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(1000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 31, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#3")
+    void findUnitsLocationsByCampaignId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByCampaignId(1, 2, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(1000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 1, 0, UTC));
+
+            UnitLocation l3 = data.get(2);
+            assertThat(l3.getUnitId()).isEqualTo(3000);
+            assertThat(l3.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#4")
+    void findUnitsLocationsByCampaignId_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findUnitsLocationsByCampaignId(1, 1, from, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#5")
+    void findUnitsLocationsByCampaignId_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 26, 0, 0, 0, 0, UTC);
+
+        repo.findUnitsLocationsByCampaignId(1, 1, from, to, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(3000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(1000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#6")
+    void findUnitsLocationsByCampaignId_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByCampaignId(10, 1, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#7")
+    void findUnitsLocationsByCampaignId_NM7(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByCampaignId(1, 0, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#8")
+    void findUnitsLocationsByCampaignId_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC);
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, to, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(3000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#9")
+    void findUnitsLocationsByCampaignId_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#10")
+    void findUnitsLocationsByCampaignId_NM10(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.DESC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 31, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#11")
+    void findUnitsLocationsByCampaignId_NM11(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#12")
+    void findUnitsLocationsByCampaignId_NM12(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.DESC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByCampaignId#3")
+    void findUnitsLocationsByCampaignId_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1002", FilterOperation.EQ, 12)
+        );
+
+        repo.findUnitsLocationsByCampaignId(1, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#1")
+    void findUnitsLocationsByIdentityAndCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(l1.getLocation()).isNotNull();
+            assertThat(l1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(l1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(l1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(l1.getLocation().getAngle()).isEqualTo(0f);
+
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#2")
+    void findUnitsLocationsByIdentityAndCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.DESC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#3")
+    void findUnitsLocationsByIdentityAndCampaignId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 2, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation l2 = data.get(1);
+            assertThat(l2.getUnitId()).isEqualTo(2000);
+            assertThat(l2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#4")
+    void findUnitsLocationsByIdentityAndCampaignId_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, from, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#5")
+    void findUnitsLocationsByIdentityAndCampaignId_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 26, 0, 0, 0, 0, UTC);
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, from, to, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#6")
+    void findUnitsLocationsByIdentityAndCampaignId_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 10, 1, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#7")
+    void findUnitsLocationsByIdentityAndCampaignId_NM7(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 0, null, null, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#8")
+    void findUnitsLocationsByIdentityAndCampaignId_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC);
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, to, SortType.ASC, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#9")
+    void findUnitsLocationsByIdentityAndCampaignId_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#10")
+    void findUnitsLocationsByIdentityAndCampaignId_NM10(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.DESC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 31, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#11")
+    void findUnitsLocationsByIdentityAndCampaignId_NM11(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#12")
+    void findUnitsLocationsByIdentityAndCampaignId_NM12(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.DESC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsLocationsByIdentityAndCampaignId#13")
+    void findUnitsLocationsByIdentityAndCampaignId_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2001", FilterOperation.EQ, 21f)
+        );
+
+        repo.findUnitsLocationsByIdentityAndCampaignId(USER_IDENTITY, 2, 1, null, null, SortType.ASC, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation l1 = data.get(0);
+            assertThat(l1.getUnitId()).isEqualTo(2000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#1")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 4, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(4);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation l1 = data.data().get(0);
+            assertThat(l1.getUnitId()).isEqualTo(1000);
+            assertThat(l1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(l1.getLocation()).isNotNull();
+            assertThat(l1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(l1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(l1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(l1.getLocation().getAngle()).isEqualTo(0f);
+
+            UnitLocation l4 = data.data().get(3);
+            assertThat(l4.getUnitId()).isEqualTo(1000);
+            assertThat(l4.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+            assertThat(l4.getLocation()).isNotNull();
+            assertThat(l4.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(l4.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(l4.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(l4.getLocation().getAngle()).isEqualTo(0f);
+
+            testContext.completeNow();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#2")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#4")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t2 = data.data().get(0);
+            assertThat(t2.getUnitId()).isEqualTo(1000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#5")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(1000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(1000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#6")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 4, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#7")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#8")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#9")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#10")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations after the end of the campaign
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#11")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(10, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#12")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 2000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#13")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+        //  No access to the unit 1000 from the campaign 2
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(2, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByCampaignIdAndUnitIdWithPaging#14")
+    void findLocationsByCampaignIdAndUnitIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1002", FilterOperation.EQ, 12f)
+        );
+
+        repo.findLocationsByCampaignIdAndUnitIdWithPaging(1, 1000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(1000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#1")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY, 2, 2000, null, null, 0, 4, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(4);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(t1.getLocation()).isNotNull();
+            assertThat(t1.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t1.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t1.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t1.getLocation().getAngle()).isEqualTo(0f);
+
+            UnitLocation t5 = data.data().get(3);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+            assertThat(t5.getLocation()).isNotNull();
+            assertThat(t5.getLocation().getLongitude()).isEqualTo(13.9f);
+            assertThat(t5.getLocation().getLatitude()).isEqualTo(49.33f);
+            assertThat(t5.getLocation().getAltitude()).isEqualTo(488f);
+            assertThat(t5.getLocation().getAngle()).isEqualTo(0f);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#2")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, null, 0, 5, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation t2 = data.data().get(4);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#3")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation t2 = data.data().get(2);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#4")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC));
+
+            UnitLocation t2 = data.data().get(1);
+            assertThat(t2.getUnitId()).isEqualTo(2000);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#5")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 25, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t1 = data.data().get(0);
+            assertThat(t1.getUnitId()).isEqualTo(2000);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+
+            UnitLocation t5 = data.data().get(2);
+            assertThat(t5.getUnitId()).isEqualTo(2000);
+            assertThat(t5.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 23, 59, 59, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#6")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 4, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#7")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#8")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 0, 1, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#9")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+        // No observations before the date 'to'.
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 20, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, to, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#10")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+        // No observations after the end of the campaign
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0, 0, 0, 0, UTC);
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#11")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+        //  Campaign ID 10 does not exist.
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,10, 2000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#12")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+        // No access to the unit 1000 from the campaign 2
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 1000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#13")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+        // No user's access to the campaign 1
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,1, 3000, null, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findLocationsByIdentityCampaignIdAndUnitIdWithPaging#14")
+    void findLocationsByIdentityCampaignIdAndUnitIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2002", FilterOperation.EQ, 22f)
+        );
+
+        repo.findLocationsByIdentityCampaignIdAndUnitIdWithPaging(USER_IDENTITY,2, 2000, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            UnitLocation t = data.data().get(0);
+            assertThat(t.getUnitId()).isEqualTo(2000);
+            assertThat(t.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0, 0, 1, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitById#1")
+    void findUnitById_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitById(1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getUnitId()).isEqualTo(1000);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getImei()).isEqualTo("imei(1000)");
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitById#2")
+    void findUnitById_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitById(5000).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '5000' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndId#1")
+    void findUnitByIdentityAndId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndId(USER_IDENTITY, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getUnitId()).isEqualTo(2000);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getImei()).isEqualTo("imei(2000)");
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndId#2")
+    void findUnitByIdentityAndId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndId(USER_IDENTITY, 1000).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '1000' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndId#3")
+    void findUnitByIdentityAndId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndId(USER_IDENTITY, 5000).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '5000' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByUnitId#1")
+    void findSensorsByUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByUnitId(1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(1001);
+            assertThat(s1.getName()).isEqualTo("sensor(1001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(1002);
+            assertThat(s2.getName()).isEqualTo("sensor(1002)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByUnitId#2")
+    void findSensorsByUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByUnitId(3000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(3001);
+            assertThat(s1.getName()).isEqualTo("sensor(3001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByUnitId#3")
+    void findSensorsByUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByUnitId(5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndUnitId#1")
+    void findSensorsByIdentityAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndUnitId(USER_IDENTITY, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(2001);
+            assertThat(s1.getName()).isEqualTo("sensor(2001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(2002);
+            assertThat(s2.getName()).isEqualTo("sensor(2002)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndUnitId#2")
+    void findSensorsByIdentityAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndUnitId(USER_IDENTITY, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndUnitId#3")
+    void findSensorsByIdentityAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndUnitId(USER_IDENTITY, 5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allUnits#1")
+    void allUnits_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allUnits().onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+
+            Unit u1 = data.get(0);
+            assertThat(u1.getUnitId()).isEqualTo(1000);
+            assertThat(u1.getImei()).isEqualTo("imei(1000)");
+            assertThat(u1.getName()).isEqualTo("mock(name)");
+            assertThat(u1.getDescription()).isEqualTo("mock(description)");
+
+            Unit u3 = data.get(2);
+            assertThat(u3.getUnitId()).isEqualTo(3000);
+            assertThat(u3.getImei()).isEqualTo("imei(3000)");
+            assertThat(u3.getName()).isEqualTo("mock(name)");
+            assertThat(u3.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allUnitsByIdentity#1")
+    void allUnitsByIdentity_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allUnitsByIdentity(USER_IDENTITY).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Unit u1 = data.get(0);
+            assertThat(u1.getUnitId()).isEqualTo(2000);
+            assertThat(u1.getImei()).isEqualTo("imei(2000)");
+            assertThat(u1.getName()).isEqualTo("mock(name)");
+            assertThat(u1.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByUnitId#1")
+    void findCampaignsByUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByUnitId(1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Campaign c = data.get(0);
+            assertThat(c.getId()).isEqualTo(1);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(c.getToTime()).isEqualTo(OffsetDateTime.of(2024, 3, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByUnitId#2")
+    void findCampaignsByUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByUnitId(2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Campaign c = data.get(0);
+            assertThat(c.getId()).isEqualTo(2);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(c.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByUnitId#3")
+    void findCampaignsByUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByUnitId(5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByIdentityAndUnitId#1")
+    void findCampaignsByIdentityAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByIdentityAndUnitId(USER_IDENTITY, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByIdentityAndUnitId#2")
+    void findCampaignsByIdentityAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByIdentityAndUnitId(USER_IDENTITY, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Campaign c = data.get(0);
+            assertThat(c.getId()).isEqualTo(2);
+            assertThat(c.getName()).isEqualTo("mock(name)");
+            assertThat(c.getDescription()).isEqualTo("mock(description)");
+            assertThat(c.getFromTime()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(c.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findCampaignsByIdentityAndUnitId#3")
+    void findCampaignsByIdentityAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findCampaignsByIdentityAndUnitId(USER_IDENTITY, 5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorById#1")
+    void findSensorById_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorById(1001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getSensorId()).isEqualTo(1001);
+            assertThat(data.getName()).isEqualTo("sensor(1001)");
+            assertThat(data.getType()).isEqualTo("mock(type)");
+            assertThat(data.getIoID()).isEqualTo(11);
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getPhenomenon()).isNotNull();
+            assertThat(data.getPhenomenon().getId()).isEqualTo(1);
+            assertThat(data.getPhenomenon().getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorById#2")
+    void findSensorById_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorById(1000).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1000' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndId#1")
+    void findSensorByIdentityAndId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndId(USER_IDENTITY, 2001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getSensorId()).isEqualTo(2001);
+            assertThat(data.getName()).isEqualTo("sensor(2001)");
+            assertThat(data.getType()).isEqualTo("mock(type)");
+            assertThat(data.getIoID()).isEqualTo(21);
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getPhenomenon()).isNotNull();
+            assertThat(data.getPhenomenon().getId()).isEqualTo(1);
+            assertThat(data.getPhenomenon().getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndId#2")
+    void findSensorByIdentityAndId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndId(USER_IDENTITY, 1000).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1000' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndId#3")
+    void findSensorByIdentityAndId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndId(USER_IDENTITY, 1001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1001' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allSensors#1")
+    void allSensors_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allSensors().onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(5);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(1001);
+            assertThat(s1.getName()).isEqualTo("sensor(1001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+            assertThat(s1.getIoID()).isEqualTo(11);
+            assertThat(s1.getDescription()).isEqualTo("mock(description)");
+            assertThat(s1.getPhenomenon()).isNotNull();
+            assertThat(s1.getPhenomenon().getId()).isEqualTo(1);
+
+            Sensor s5 = data.get(4);
+            assertThat(s5.getSensorId()).isEqualTo(3001);
+            assertThat(s5.getName()).isEqualTo("sensor(3001)");
+            assertThat(s5.getType()).isEqualTo("mock(type)");
+            assertThat(s5.getIoID()).isEqualTo(31);
+            assertThat(s5.getDescription()).isEqualTo("mock(description)");
+            assertThat(s5.getPhenomenon()).isNotNull();
+            assertThat(s5.getPhenomenon().getId()).isEqualTo(3);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allSensorsByIdentity#1")
+    void allSensorsByIdentity_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allSensorsByIdentity(USER_IDENTITY).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(2001);
+            assertThat(s1.getName()).isEqualTo("sensor(2001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+            assertThat(s1.getIoID()).isEqualTo(21);
+            assertThat(s1.getDescription()).isEqualTo("mock(description)");
+            assertThat(s1.getPhenomenon()).isNotNull();
+            assertThat(s1.getPhenomenon().getId()).isEqualTo(1);
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(2002);
+            assertThat(s2.getName()).isEqualTo("sensor(2002)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+            assertThat(s2.getIoID()).isEqualTo(22);
+            assertThat(s2.getDescription()).isEqualTo("mock(description)");
+            assertThat(s2.getPhenomenon()).isNotNull();
+            assertThat(s2.getPhenomenon().getId()).isEqualTo(2);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsBySensorId#1")
+    void findUnitsBySensorId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsBySensorId(1001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Unit u = data.get(0);
+            assertThat(u.getUnitId()).isEqualTo(1000);
+            assertThat(u.getImei()).isEqualTo("imei(1000)");
+            assertThat(u.getName()).isEqualTo("mock(name)");
+            assertThat(u.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsBySensorId#2")
+    void findUnitsBySensorId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsBySensorId(1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByIdentityAndSensorId#1")
+    void findUnitsByIdentityAndSensorId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByIdentityAndSensorId(USER_IDENTITY, 2002).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Unit u = data.get(0);
+            assertThat(u.getUnitId()).isEqualTo(2000);
+            assertThat(u.getImei()).isEqualTo("imei(2000)");
+            assertThat(u.getName()).isEqualTo("mock(name)");
+            assertThat(u.getDescription()).isEqualTo("mock(description)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByIdentityAndSensorId#2")
+    void findUnitsByIdentityAndSensorId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByIdentityAndSensorId(USER_IDENTITY, 1001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitsByIdentityAndSensorId#3")
+    void findUnitsByIdentityAndSensorId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitsByIdentityAndSensorId(USER_IDENTITY, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findPhenomenonById#1")
+    void findPhenomenonById_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findPhenomenonById(1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getId()).isEqualTo(1);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getUom()).isEqualTo("mock(uom)");
+            assertThat(data.getUomLink()).isEqualTo("mock(uom_link)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findPhenomenonById#2")
+    void findPhenomenonById_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findPhenomenonById(4).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Phenomenon ID '4' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findPhenomenonById#3")
+    void findPhenomenonById_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findPhenomenonById(3).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getId()).isEqualTo(3);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findPhenomenonByIdentityAndId#1")
+    void findPhenomenonByIdentityAndId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findPhenomenonByIdentityAndId(USER_IDENTITY, 1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getId()).isEqualTo(1);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getUom()).isEqualTo("mock(uom)");
+            assertThat(data.getUomLink()).isEqualTo("mock(uom_link)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findPhenomenonByIdentityAndId#2")
+    void findPhenomenonByIdentityAndId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findPhenomenonByIdentityAndId(USER_IDENTITY, 3).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Phenomenon ID '3' not found.");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allPhenomenons#1")
+    void allPhenomenons_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allPhenomenons().onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+
+            Phenomenon p1 = data.get(0);
+            assertThat(p1.getId()).isEqualTo(1);
+            assertThat(p1.getName()).isEqualTo("mock(name)");
+
+            Phenomenon p3 = data.get(2);
+            assertThat(p3.getId()).isEqualTo(3);
+            assertThat(p3.getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: allPhenomenonsByIdentity#1")
+    void allPhenomenonsByIdentity_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.allPhenomenonsByIdentity(USER_IDENTITY).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Phenomenon p1 = data.get(0);
+            assertThat(p1.getId()).isEqualTo(1);
+            assertThat(p1.getName()).isEqualTo("mock(name)");
+
+            Phenomenon p2 = data.get(1);
+            assertThat(p2.getId()).isEqualTo(2);
+            assertThat(p2.getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByPhenomenonId#1")
+    void findSensorsByPhenomenonId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByPhenomenonId(1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(1001);
+            assertThat(s1.getName()).isEqualTo("sensor(1001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(2001);
+            assertThat(s2.getName()).isEqualTo("sensor(2001)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByPhenomenonId#2")
+    void findSensorsByPhenomenonId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByPhenomenonId(3).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(3001);
+            assertThat(s1.getName()).isEqualTo("sensor(3001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByPhenomenonId#3")
+    void findSensorsByPhenomenonId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByPhenomenonId(4).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndPhenomenonId#1")
+    void findSensorsByIdentityAndPhenomenonId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndPhenomenonId(USER_IDENTITY, 1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(2001);
+            assertThat(s1.getName()).isEqualTo("sensor(2001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndPhenomenonId#2")
+    void findSensorsByIdentityAndPhenomenonId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndPhenomenonId(USER_IDENTITY, 3).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndPhenomenonId#3")
+    void findSensorsByIdentityAndPhenomenonId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByPhenomenonId(4).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdAndCampaignId#1")
+    void findUnitByIdAndCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdAndCampaignId(1000, 1).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getUnitId()).isEqualTo(1000);
+            assertThat(data.getCampaignId()).isEqualTo(1);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getImei()).isEqualTo("imei(1000)");
+            assertThat(data.getType()).isEqualTo("T");
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getFromTime()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(data.getToTime()).isEqualTo(OffsetDateTime.of(2024, 3, 1, 0, 0, 0, 0, UTC));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdAndCampaignId#2")
+    void findUnitByIdAndCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdAndCampaignId(1000, 2).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '1000' not found for Campaign(2).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdAndCampaignId#3")
+    void findUnitByIdAndCampaignId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdAndCampaignId(2000, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '2000' not found for Campaign(1).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdAndCampaignId#4")
+    void findUnitByIdAndCampaignId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdAndCampaignId(5000, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '5000' not found for Campaign(1).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndIdAndCampaignId#1")
+    void findUnitByIdentityAndIdAndCampaignId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndIdAndCampaignId(USER_IDENTITY, 2000, 2).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getUnitId()).isEqualTo(2000);
+            assertThat(data.getCampaignId()).isEqualTo(2);
+            assertThat(data.getName()).isEqualTo("mock(name)");
+            assertThat(data.getImei()).isEqualTo("imei(2000)");
+            assertThat(data.getType()).isEqualTo("T");
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getFromTime()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0, 0, 0, 0, UTC));
+            assertThat(data.getToTime()).isNull();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndIdAndCampaignId#2")
+    void findUnitByIdentityAndIdAndCampaignId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndIdAndCampaignId(USER_IDENTITY, 1000, 2).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '1000' not found for Campaign(2).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndIdAndCampaignId#3")
+    void findUnitByIdentityAndIdAndCampaignId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndIdAndCampaignId(USER_IDENTITY, 2000, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '2000' not found for Campaign(1).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndIdAndCampaignId#4")
+    void findUnitByIdentityAndIdAndCampaignId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndIdAndCampaignId(USER_IDENTITY, 5000, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '5000' not found for Campaign(1).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findUnitByIdentityAndIdAndCampaignId#5")
+    void findUnitByIdentityAndIdAndCampaignId_NM5(Vertx vertx, VertxTestContext testContext) {
+        repo.findUnitByIdentityAndIdAndCampaignId(USER_IDENTITY, 1000, 1).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Unit ID '1000' not found for Campaign(1).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByCampaignIdAndUnitId#1")
+    void findSensorsByCampaignIdAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByCampaignIdAndUnitId(1, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(1001);
+            assertThat(s1.getName()).isEqualTo("sensor(1001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(1002);
+            assertThat(s2.getName()).isEqualTo("sensor(1002)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByCampaignIdAndUnitId#2")
+    void findSensorsByCampaignIdAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByCampaignIdAndUnitId(1, 3000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(3001);
+            assertThat(s1.getName()).isEqualTo("sensor(3001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByCampaignIdAndUnitId#3")
+    void findSensorsByCampaignIdAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByCampaignIdAndUnitId(1, 5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByCampaignIdAndUnitId#4")
+    void findSensorsByCampaignIdAndUnitId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByCampaignIdAndUnitId(5, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#1")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+
+            Sensor s1 = data.get(0);
+            assertThat(s1.getSensorId()).isEqualTo(2001);
+            assertThat(s1.getName()).isEqualTo("sensor(2001)");
+            assertThat(s1.getType()).isEqualTo("mock(type)");
+
+            Sensor s2 = data.get(1);
+            assertThat(s2.getSensorId()).isEqualTo(2002);
+            assertThat(s2.getName()).isEqualTo("sensor(2002)");
+            assertThat(s2.getType()).isEqualTo("mock(type)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#2")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 1, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#3")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 1, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#4")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 1000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#5")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM5(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 5000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorsByIdentityAndCampaignIdAndUnitId#6")
+    void findSensorsByIdentityAndCampaignIdAndUnitId_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorsByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 5, 2000).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByCampaignIdAndUnitId#1")
+    void findSensorByCampaignIdAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByCampaignIdAndUnitId(1, 1000, 1001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getSensorId()).isEqualTo(1001);
+            assertThat(data.getName()).isEqualTo("sensor(1001)");
+            assertThat(data.getType()).isEqualTo("mock(type)");
+            assertThat(data.getIoID()).isEqualTo(11);
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getPhenomenon()).isNotNull();
+            assertThat(data.getPhenomenon().getId()).isEqualTo(1);
+            assertThat(data.getPhenomenon().getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByCampaignIdAndUnitId#2")
+    void findSensorByCampaignIdAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByCampaignIdAndUnitId(1, 1000, 2001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '2001' not found in Campaign(1) and Unit(1000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByCampaignIdAndUnitId#3")
+    void findSensorByCampaignIdAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByCampaignIdAndUnitId(1, 2000, 1001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1001' not found in Campaign(1) and Unit(2000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByCampaignIdAndUnitId#4")
+    void findSensorByCampaignIdAndUnitId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByCampaignIdAndUnitId(2, 1000, 1001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1001' not found in Campaign(2) and Unit(1000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndCampaignIdAndUnitId#1")
+    void findSensorByIdentityAndCampaignIdAndUnitId_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 2000, 2001).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.getSensorId()).isEqualTo(2001);
+            assertThat(data.getName()).isEqualTo("sensor(2001)");
+            assertThat(data.getType()).isEqualTo("mock(type)");
+            assertThat(data.getIoID()).isEqualTo(21);
+            assertThat(data.getDescription()).isEqualTo("mock(description)");
+            assertThat(data.getPhenomenon()).isNotNull();
+            assertThat(data.getPhenomenon().getId()).isEqualTo(1);
+            assertThat(data.getPhenomenon().getName()).isEqualTo("mock(name)");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndCampaignIdAndUnitId#2")
+    void findSensorByIdentityAndCampaignIdAndUnitId_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndCampaignIdAndUnitId(USER_IDENTITY,1, 1000, 1001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1001' not found in Campaign(1) and Unit(1000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndCampaignIdAndUnitId#3")
+    void findSensorByIdentityAndCampaignIdAndUnitId_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 2000, 1001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '1001' not found in Campaign(2) and Unit(2000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndCampaignIdAndUnitId#4")
+    void findSensorByIdentityAndCampaignIdAndUnitId_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 2, 1000, 2001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '2001' not found in Campaign(2) and Unit(1000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findSensorByIdentityAndCampaignIdAndUnitId#5")
+    void findSensorByIdentityAndCampaignIdAndUnitId_NM5(Vertx vertx, VertxTestContext testContext) {
+        repo.findSensorByIdentityAndCampaignIdAndUnitId(USER_IDENTITY, 1, 2000, 2001).onComplete(testContext.failing(th -> testContext.verify(() -> {
+            assertThat(th).isInstanceOf(DataNotFoundException.class);
+            assertThat(th.getMessage()).isEqualTo("Sensor ID '2001' not found in Campaign(1) and Unit(2000).");
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#1")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(11.11);
+
+            SensorTelemetry t2 = data.data().get(1);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(11d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#2")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 3000, 3001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(JsonArray.of(1, 2, 3, 4));
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#3")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1002, null, null, 1, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,1,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(0d);
+
+            SensorTelemetry t2 = data.data().get(2);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,1,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(12d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#4")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 3001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#5")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 3000, 1001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#6")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(2, 1000, 1001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#7")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0,0,0,0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(2, 1000, 1001, from, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#8")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1001, from, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(11d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#9")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0,0,0,0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1001, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(11.11);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#10")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0,0,0,0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1002, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(12.12);
+
+            SensorTelemetry t2 = data.data().get(1);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,1,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(0d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#11")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC);
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1002, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#12")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1001, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(11d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#13")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1002, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,1,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(12d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#14")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1001", FilterOperation.LT, 12f),
+            Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1001, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(11d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#15")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM15(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "1005", FilterOperation.GT, 10f)
+        );
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1005, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging#16")
+    void findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging_NM16(Vertx vertx, VertxTestContext testContext) {
+
+        repo.findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(1, 1000, 1005, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#1")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM1(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(3);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(21.21);
+
+            SensorTelemetry t2 = data.data().get(1);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(21d);
+
+            SensorTelemetry t3 = data.data().get(2);
+            assertThat(t3.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0,0,0,0, UTC));
+            assertThat(t3.getValue()).isEqualTo(21.21);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#2")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM2(Vertx vertx, VertxTestContext testContext) {
+        testContext.completeNow();
+        /* TODO create sensor with observedValues as Array [1, 2, 3, 4]
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 3001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(JsonArray.of(1, 2, 3, 4));
+
+            testContext.completeNow();
+        })));
+         */
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#3")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM3(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2002, null, null, 1, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(4);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,1,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(0d);
+
+            SensorTelemetry t2 = data.data().get(3);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0,0,0,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(22.22);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#4")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM4(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 3001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#5")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM5(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 3000, 2001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#6")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM6(Vertx vertx, VertxTestContext testContext) {
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 1, 2000, 2001, null, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#7")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM7(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 2, 1, 0,0,0,0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 1, 2000, 2001, from, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#8")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM8(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2001, from, null, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(21d);
+
+            SensorTelemetry t2 = data.data().get(1);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 3, 15, 0,0,0,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(21.21);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#9")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM9(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0,0,0,0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2001, from, null, 0, 1, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isTrue();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(21.21);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#10")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM10(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime from = OffsetDateTime.of(2023, 12, 20, 0,0,0,0, UTC);
+        OffsetDateTime to = OffsetDateTime.of(2024, 1, 1, 0, 0, 0, 0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2002, from, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(2);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(22.22);
+
+            SensorTelemetry t2 = data.data().get(1);
+            assertThat(t2.getTimestamp()).isEqualTo(OffsetDateTime.of(2023, 12, 25, 0,0,1,0, UTC));
+            assertThat(t2.getValue()).isEqualTo(0d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#11")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM11(Vertx vertx, VertxTestContext testContext) {
+
+        OffsetDateTime to = OffsetDateTime.of(2023, 12, 24, 0,0,0,0, UTC);
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2002, null, to, 0, 10, emptyList()).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#12")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM12(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2001, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(21d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#13")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM13(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LONGITUDE, null, FilterOperation.GT, 15.f),
+                Filter.of(FilterType.UNIT, FilterAttribute.LATITUDE, null, FilterOperation.LT, 45.f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2002, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,1,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(22d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#14")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM14(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2001", FilterOperation.LT, 32f),
+                Filter.of(FilterType.UNIT, FilterAttribute.SPEED, null, FilterOperation.NE, 0f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2001, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(1);
+            assertThat(data.hasNext()).isFalse();
+
+            SensorTelemetry t1 = data.data().get(0);
+            assertThat(t1.getTimestamp()).isEqualTo(OffsetDateTime.of(2024, 1, 1, 0,0,0,0, UTC));
+            assertThat(t1.getValue()).isEqualTo(21d);
+
+            testContext.completeNow();
+        })));
+    }
+
+    @Test
+    @DisplayName("DB Test: findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging#15")
+    void findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging_NM15(Vertx vertx, VertxTestContext testContext) {
+
+        List<Filter> filters = List.of(
+                Filter.of(FilterType.SENSOR, FilterAttribute.ID, "2005", FilterOperation.GT, 10f)
+        );
+
+        repo.findObservationsByIdentityAndCampaignIdAndUnitIdAndSensorIdWithPaging(USER_IDENTITY, 2, 2000, 2005, null, null, 0, 10, filters).onComplete(testContext.succeeding(data -> testContext.verify(() -> {
+            assertThat(data.size()).isEqualTo(0);
+            assertThat(data.hasNext()).isFalse();
+
+            testContext.completeNow();
+        })));
+    }
 }