|
|
@@ -1,38 +1,204 @@
|
|
|
package cz.senslog.analytics.utils;
|
|
|
|
|
|
import cz.senslog.analytics.domain.*;
|
|
|
+import cz.senslog.analytics.domain.ComparisonOperator;
|
|
|
import cz.senslog.analytics.utils.validator.NotifyTrigger;
|
|
|
import cz.senslog.analytics.utils.validator.ThresholdChecker;
|
|
|
import cz.senslog.analytics.utils.validator.Validator;
|
|
|
+import cz.senslog.analytics.utils.validator.domain.ValidationResult;
|
|
|
+import org.junit.jupiter.api.BeforeAll;
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
import java.time.OffsetDateTime;
|
|
|
+import java.time.ZoneOffset;
|
|
|
import java.util.List;
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
+import static cz.senslog.analytics.domain.AttributeType.TIME;
|
|
|
+import static cz.senslog.analytics.domain.AttributeType.VAL;
|
|
|
+import static cz.senslog.analytics.domain.ComparisonOperator.*;
|
|
|
+import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.INSTANT;
|
|
|
+import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.ON_CHANGE;
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
|
|
class ThresholdCheckerTest {
|
|
|
|
|
|
private static final Sensor SENSOR = new Sensor(100L, 1000L, 1001L);
|
|
|
|
|
|
+ private static Validator<Observation> validator;
|
|
|
+
|
|
|
+ @BeforeAll
|
|
|
+ static void setUp() {
|
|
|
+ validator = Validator.<Observation>create()
|
|
|
+ .addMapping(VAL, o -> o::value)
|
|
|
+ .addMapping(AttributeType.TIME, o -> () -> (double) o.timestamp().toLocalTime().toSecondOfDay());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void test_1() {
|
|
|
+
|
|
|
+ final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+ final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+
|
|
|
+
|
|
|
+ ThresholdChecker<Observation> checker = ThresholdChecker.create(
|
|
|
+ List.of(
|
|
|
+ Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, INSTANT, VAL, List.of(
|
|
|
+ ThresholdDimensionRule.of(GT, 25)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ThresholdDimension.of(2, INSTANT, TIME, List.of(
|
|
|
+ ThresholdDimensionRule.of(GE, 43200),
|
|
|
+ ThresholdDimensionRule.of(LE, 54000)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ))
|
|
|
+ ),
|
|
|
+ validator,
|
|
|
+ (alert) -> {
|
|
|
+ assertEquals(SENSOR.id(), alert.datasourceId());
|
|
|
+ assertFalse(alert.timestamp().isBefore(startInterval) || alert.timestamp().isAfter(endInterval));
|
|
|
+ assertEquals(2, alert.violatedData().length);
|
|
|
+ },
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Observation> passedObservations = Stream.of(
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
|
|
|
+ ).filter(checker::check).toList();
|
|
|
+
|
|
|
+ assertEquals(2, passedObservations.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void test_2() {
|
|
|
+
|
|
|
+ final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+ final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+
|
|
|
+
|
|
|
+ ThresholdChecker<Observation> checker = ThresholdChecker.create(
|
|
|
+ List.of(
|
|
|
+ Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, ON_CHANGE, VAL, List.of(
|
|
|
+ ThresholdDimensionRule.of(GT, 25)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ThresholdDimension.of(2, INSTANT, TIME, List.of(
|
|
|
+ ThresholdDimensionRule.of(GE, 43200),
|
|
|
+ ThresholdDimensionRule.of(LE, 54000)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )),
|
|
|
+ Threshold.of(2, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(3, INSTANT, VAL, List.of(
|
|
|
+ ThresholdDimensionRule.of(GT, 25)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ThresholdDimension.of(4, ON_CHANGE, TIME, List.of(
|
|
|
+ ThresholdDimensionRule.of(GE, 43200),
|
|
|
+ ThresholdDimensionRule.of(LE, 54000)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ))
|
|
|
+ ),
|
|
|
+ validator,
|
|
|
+ (alert) -> {
|
|
|
+ assertEquals(SENSOR.id(), alert.datasourceId());
|
|
|
+ assertTrue(alert.timestamp().isEqual(startInterval.plusSeconds(1)) || alert.timestamp().isEqual(endInterval));
|
|
|
+ assertEquals(2, alert.violatedData().length);
|
|
|
+ },
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Observation> passedObservations = Stream.of(
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
|
|
|
+ ).filter(checker::check).toList();
|
|
|
+
|
|
|
+ assertEquals(4, passedObservations.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void test_3() {
|
|
|
+
|
|
|
+ final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+ final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
|
|
|
+
|
|
|
+
|
|
|
+ ThresholdChecker<Observation> checker = ThresholdChecker.create(
|
|
|
+ List.of(
|
|
|
+ Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, ON_CHANGE, VAL, List.of(
|
|
|
+ ThresholdDimensionRule.of(GT, 25)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ThresholdDimension.of(2, INSTANT, TIME, List.of(
|
|
|
+ ThresholdDimensionRule.of(GE, 43200),
|
|
|
+ ThresholdDimensionRule.of(LE, 54000)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )),
|
|
|
+ Threshold.of(2, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(3, INSTANT, VAL, List.of(
|
|
|
+ ThresholdDimensionRule.of(GT, 25)
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ThresholdDimension.of(4, ON_CHANGE, TIME, List.of(
|
|
|
+ ThresholdDimensionRule.of(GE, 43200),
|
|
|
+ ThresholdDimensionRule.of(LE, 54000)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ))
|
|
|
+ ),
|
|
|
+ validator,
|
|
|
+ (alert) -> {
|
|
|
+ assertEquals(SENSOR.id(), alert.datasourceId());
|
|
|
+ assertTrue(alert.timestamp().isEqual(startInterval) || alert.timestamp().isEqual(endInterval));
|
|
|
+ assertEquals(2, alert.violatedData().length);
|
|
|
+ },
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Observation> passedObservations = Stream.of(
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* fail | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
|
|
|
+ /* pass | none */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
|
|
|
+ ).filter(checker::check).toList();
|
|
|
+
|
|
|
+ assertEquals(3, passedObservations.size());
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
void check_size_1_INSTANT() {
|
|
|
|
|
|
ThresholdChecker<Observation> checker = new ThresholdChecker<>(
|
|
|
List.of(
|
|
|
- new Threshold(1, SENSOR.id(), NotifyTrigger.Mode.INSTANT, AttributeType.VAL, true, false, List.of(
|
|
|
- new ThresholdRule(ThresholdMode.GT, 10.0)
|
|
|
+ new Threshold(1, SENSOR.id(), true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, INSTANT, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
|
|
|
))
|
|
|
),
|
|
|
Validator.<Observation>create()
|
|
|
- .addMapping(AttributeType.VAL, o -> o::value),
|
|
|
+ .addMapping(VAL, o -> o::value),
|
|
|
(report) -> {
|
|
|
assertEquals(SENSOR.id(), report.datasourceId());
|
|
|
assertEquals(1, report.violatedData().length);
|
|
|
ValidationResult valResult = report.violatedData()[0];
|
|
|
- assertEquals(AttributeType.VAL, valResult.attributeType());
|
|
|
+ assertEquals(VAL, valResult.attributeType());
|
|
|
assertEquals(23.3, valResult.validatedValue());
|
|
|
assertEquals(1, valResult.records().size());
|
|
|
},
|
|
|
@@ -50,17 +216,17 @@ class ThresholdCheckerTest {
|
|
|
|
|
|
ThresholdChecker<Observation> checker = new ThresholdChecker<>(
|
|
|
List.of(
|
|
|
- new Threshold(1, SENSOR.id(), NotifyTrigger.Mode.ON_CHANGE, AttributeType.VAL, true, false, List.of(
|
|
|
- new ThresholdRule(ThresholdMode.GT, 10.0)
|
|
|
+ new Threshold(1, SENSOR.id(), true,false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
|
|
|
))
|
|
|
),
|
|
|
Validator.<Observation>create()
|
|
|
- .addMapping(AttributeType.VAL, o -> o::value),
|
|
|
+ .addMapping(VAL, o -> o::value),
|
|
|
(report) -> {
|
|
|
assertEquals(SENSOR.id(), report.datasourceId());
|
|
|
assertEquals(1, report.violatedData().length);
|
|
|
ValidationResult valResult = report.violatedData()[0];
|
|
|
- assertEquals(AttributeType.VAL, valResult.attributeType());
|
|
|
+ assertEquals(VAL, valResult.attributeType());
|
|
|
assertEquals(25.3, valResult.validatedValue());
|
|
|
assertEquals(1, valResult.records().size());
|
|
|
},
|
|
|
@@ -80,17 +246,17 @@ class ThresholdCheckerTest {
|
|
|
AtomicInteger reportStep = new AtomicInteger(1);
|
|
|
ThresholdChecker<Observation> checker = new ThresholdChecker<>(
|
|
|
List.of(
|
|
|
- new Threshold(1, SENSOR.id(), NotifyTrigger.Mode.ON_CHANGE, AttributeType.VAL, true, false, List.of(
|
|
|
- new ThresholdRule(ThresholdMode.GT, 10.0)
|
|
|
+ new Threshold(1, SENSOR.id(), true,false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
|
|
|
))
|
|
|
),
|
|
|
Validator.<Observation>create()
|
|
|
- .addMapping(AttributeType.VAL, o -> o::value),
|
|
|
+ .addMapping(VAL, o -> o::value),
|
|
|
(report) -> {
|
|
|
assertEquals(SENSOR.id(), report.datasourceId());
|
|
|
assertEquals(1, report.violatedData().length);
|
|
|
ValidationResult valResult = report.violatedData()[0];
|
|
|
- assertEquals(AttributeType.VAL, valResult.attributeType());
|
|
|
+ assertEquals(VAL, valResult.attributeType());
|
|
|
|
|
|
if (reportStep.compareAndSet(1, 2)) {
|
|
|
assertEquals(25.3, valResult.validatedValue());
|
|
|
@@ -121,8 +287,12 @@ class ThresholdCheckerTest {
|
|
|
|
|
|
ThresholdChecker<DoubleStatistics> checker = new ThresholdChecker<>(
|
|
|
List.of(
|
|
|
- new Threshold(1, -1, NotifyTrigger.Mode.INSTANT, AttributeType.MIN, true, false, List.of(new ThresholdRule(ThresholdMode.LT, 0.0))),
|
|
|
- new Threshold(1, -1, NotifyTrigger.Mode.INSTANT, AttributeType.MAX, true, false, List.of(new ThresholdRule(ThresholdMode.GT, 5.0)))
|
|
|
+ new Threshold(1, -1, true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(1, INSTANT, AttributeType.MIN, List.of(ThresholdDimensionRule.of( ComparisonOperator.LT, 0.0))))
|
|
|
+ ),
|
|
|
+ new Threshold(1, -1, true, false, true, null, List.of(
|
|
|
+ ThresholdDimension.of(2, INSTANT, AttributeType.MAX, List.of(ThresholdDimensionRule.of(GT, 5.0))))
|
|
|
+ )
|
|
|
),
|
|
|
Validator.<DoubleStatistics>create()
|
|
|
.addMapping(AttributeType.MIN, s -> s::min)
|