You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@hertzbeat.apache.org by go...@apache.org on 2025/02/06 07:51:05 UTC

(hertzbeat) 04/04: [alert] add multi query expr threshold unit test

This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch multi-query
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git

commit b2c5957d3dc6e160a516be8e01f91e5cbbc50d5c
Author: tomsun28 <to...@outlook.com>
AuthorDate: Thu Feb 6 15:47:09 2025 +0800

    [alert] add multi query expr threshold unit test
    
    Signed-off-by: tomsun28 <to...@outlook.com>
---
 .../alert/service/impl/DataSourceServiceImpl.java  | 148 +++++++++--
 .../alert/service/DataSourceServiceTest.java       | 278 ++++++++++++++++++++-
 2 files changed, 399 insertions(+), 27 deletions(-)

diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java
index a82518c902..5c241e5605 100644
--- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java
+++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/DataSourceServiceImpl.java
@@ -18,11 +18,8 @@
 package org.apache.hertzbeat.alert.service.impl;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.Set;
 import java.util.Stack;
-import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.alert.service.DataSourceService;
 import org.apache.hertzbeat.warehouse.db.QueryExecutor;
@@ -46,7 +43,7 @@ public class DataSourceServiceImpl implements DataSourceService {
     @Autowired(required = false)
     private List<QueryExecutor> executors;
     
-    private static final Pattern EXPR_TOKEN = Pattern.compile("\\(|\\)|[a-zA-Z_][a-zA-Z0-9_{}\\[\\]\".]*|\\d+(\\.\\d+)?|>=|<=|==|!=|>|<|and|or|unless");
+    private static final Pattern EXPR_TOKEN = Pattern.compile("\\(|\\)|[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*|\\d+(\\.\\d+)?|>=|<=|==|!=|>|<|and|or|unless");
     private static final String THRESHOLD = "__threshold__";
     private static final String VALUE = "__value__";
     
@@ -63,7 +60,7 @@ public class DataSourceServiceImpl implements DataSourceService {
             throw new IllegalArgumentException("Unsupported datasource: " + datasource);
         }
         // replace all white space
-        expr = expr.replaceAll("\\s+", "");
+        expr = expr.replaceAll("\\s+", " ");
         try {
             return evaluate(expr, executor);
         } catch (Exception e) {
@@ -89,14 +86,6 @@ public class DataSourceServiceImpl implements DataSourceService {
                 }
                 // remove the left parenthesis
                 operators.pop();
-            }  else if (token.matches("\\d+(\\.\\d+)?")) { 
-                double value = Double.parseDouble(token);
-                List<Map<String, Object>> numAsList = new ArrayList<>();
-                numAsList.add(Map.of(THRESHOLD, value));
-                values.push(numAsList);
-            } else if (token.matches("[a-zA-Z_][a-zA-Z0-9_{}\\[\\]\".]*")) {
-                List<Map<String, Object>> results = executor.execute(token);
-                values.push(results);
             } else if (token.matches(">=|<=|==|!=|>|<")) {
                 operators.push(token);
             } else if (token.equals("and") || token.equals("or") || token.equals("unless")) {
@@ -104,6 +93,14 @@ public class DataSourceServiceImpl implements DataSourceService {
                     applyOperator(values, operators.pop());
                 }
                 operators.push(token);
+            } else if (token.matches("\\d+(\\.\\d+)?")) { 
+                double value = Double.parseDouble(token);
+                List<Map<String, Object>> numAsList = new ArrayList<>();
+                numAsList.add(Map.of(THRESHOLD, value));
+                values.push(numAsList);
+            } else if (token.matches("[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*")) {
+                List<Map<String, Object>> results = executor.execute(token);
+                values.push(results);
             }
         }
         while (!operators.isEmpty()) {
@@ -114,15 +111,18 @@ public class DataSourceServiceImpl implements DataSourceService {
 
     private int precedence(String op) {
         return switch (op) {
-            case "or", "unless" -> 1;
-            case "and" -> 2;
-            case ">", "<", ">=", "<=", "==", "!=" -> 3;
+            case "or" -> 1;
+            case "unless" -> 2;
+            case "and" -> 3;
+            case ">", "<", ">=", "<=", "==", "!=" -> 4;
             default -> 0;
         };
     }
 
     private void applyOperator(Stack<List<Map<String, Object>>> values, String op) {
-        if (values.size() < 2) return;
+        if (values.size() < 2) {
+            return;
+        };
         List<Map<String, Object>> rightOperand = values.pop();
         List<Map<String, Object>> leftOperand = values.pop();
         if (rightOperand.size() == 1 && rightOperand.get(0).containsKey(THRESHOLD)) {
@@ -146,15 +146,117 @@ public class DataSourceServiceImpl implements DataSourceService {
             }
             return;
         }
-        
+        Map<String, Object> leftMap = null;
+        boolean leftMatch = false;
+        Map<String, Object> rightMap = null;
+        boolean rightMatch = false;
         switch (op) {
-            case "and" -> values.push(leftOperand.stream().filter(rightOperand::contains).collect(Collectors.toList()));
+            case "and" -> {
+                for (Map<String, Object> item : leftOperand) {
+                    if (leftMap == null) {
+                        leftMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        leftMap = item;
+                        leftMatch = true;
+                        break;
+                    }
+                }
+                for (Map<String, Object> item : rightOperand) {
+                    if (rightMap == null) {
+                        rightMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        rightMap = item;
+                        rightMatch = true;
+                        break;
+                    }
+                }
+                if (leftMatch && rightMatch) {
+                    rightMap.putAll(leftMap);
+                    values.push(new LinkedList<>(List.of(rightMap)));
+                } else if (leftMap != null) {
+                    leftMap.put(VALUE, null);
+                    values.push(new LinkedList<>(List.of(leftMap)));
+                } else if (rightMap != null) {
+                    rightMap.put(VALUE, null);
+                    values.push(new LinkedList<>(List.of(rightMap)));
+                } 
+            }
             case "or" -> {
-                Set<Map<String, Object>> resultSet = new HashSet<>(leftOperand);
-                resultSet.addAll(rightOperand);
-                values.push(new ArrayList<>(resultSet));
+                for (Map<String, Object> item : leftOperand) {
+                    if (leftMap == null) {
+                        leftMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        leftMap = item;
+                        leftMatch = true;
+                        break;
+                    }
+                }
+                for (Map<String, Object> item : rightOperand) {
+                    if (rightMap == null) {
+                        rightMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        rightMap = item;
+                        rightMatch = true;
+                        break;
+                    }
+                }
+                if (leftMatch && rightMatch) {
+                    rightMap.putAll(leftMap);
+                    values.push(new LinkedList<>(List.of(rightMap)));
+                } else if (leftMatch) {
+                    values.push(new LinkedList<>(List.of(leftMap)));
+                } else if (rightMatch) {
+                    values.push(new LinkedList<>(List.of(rightMap)));
+                } else {
+                    if (leftMap != null && rightMap != null) {
+                        rightMap.putAll(leftMap);
+                        values.push(new LinkedList<>(List.of(rightMap)));
+                    } else if (leftMap != null) {
+                        values.push(new LinkedList<>(List.of(leftMap)));
+                    } else if (rightMap != null){
+                        values.push(new LinkedList<>(List.of(rightMap)));
+                    }
+                }
+            }
+            case "unless" -> {
+                for (Map<String, Object> item : leftOperand) {
+                    if (leftMap == null) {
+                        leftMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        leftMap = item;
+                        leftMatch = true;
+                        break;
+                    }
+                }
+                for (Map<String, Object> item : rightOperand) {
+                    if (rightMap == null) {
+                        rightMap = item;
+                    }
+                    if (item.get(VALUE) != null) {
+                        rightMap = item;
+                        rightMatch = true;
+                        break;
+                    }
+                }
+                if (leftMatch && !rightMatch) {
+                    values.push(new LinkedList<>(List.of(leftMap)));
+                } else {
+                    if (leftMap != null) {
+                        leftMap.put(VALUE, null);
+                        values.push(new LinkedList<>(List.of(leftMap)));
+                    } else {
+                        if (rightMap != null) {
+                            rightMap.put(VALUE, null);
+                            values.push(new LinkedList<>(List.of(rightMap)));
+                        } 
+                    }
+                }
             }
-            case "unless" -> values.push(leftOperand.stream().filter(item -> !rightOperand.contains(item)).collect(Collectors.toList()));
             default -> throw new IllegalArgumentException("Unsupported operator: " + op);
         }
     }
diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/DataSourceServiceTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/DataSourceServiceTest.java
index 812f231bd6..129759e4bd 100644
--- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/DataSourceServiceTest.java
+++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/DataSourceServiceTest.java
@@ -1,6 +1,7 @@
 package org.apache.hertzbeat.alert.service;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import java.util.HashMap;
 import org.apache.hertzbeat.alert.service.impl.DataSourceServiceImpl;
@@ -216,9 +217,8 @@ class DataSourceServiceTest {
         dataSourceService.setExecutors(List.of(mockExecutor));
 
         List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 120");
-        assertEquals(2, result.size());
+        assertEquals(1, result.size());
         assertEquals(100.0, result.get(0).get("__value__"));
-        assertNull(result.get(1).get("__value__"));
     }
 
     @Test
@@ -234,8 +234,278 @@ class DataSourceServiceTest {
         dataSourceService.setExecutors(List.of(mockExecutor));
 
         List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 50) and (node_cpu_seconds_total{mode=\"idle\"} < 120)");
-        assertEquals(2, result.size());
+        assertEquals(1, result.size());
         assertEquals(100.0, result.get(0).get("__value__"));
-        assertNull(result.get(1).get("__value__"));
+    }
+
+    @Test
+    void calculate13() {
+        List<Map<String, Object>> prometheusData = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 and node_cpu_seconds_total{mode=\"idle\"} < 220");
+        assertEquals(1, result.size());
+        assertEquals(200.0, result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate14() {
+        List<Map<String, Object>> prometheusData = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 150) and (node_cpu_seconds_total{mode=\"idle\"} < 220)");
+        assertEquals(1, result.size());
+        assertEquals(200.0, result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate15() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 and node_cpu_seconds_total{mode=\"idle\"} < 220");
+        assertEquals(1, result.size());
+        assertNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate16() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(1, result.size());
+        assertNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate17() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 or node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(1, result.size());
+        assertEquals(200.0, result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate18() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 120");
+        assertEquals(1, result.size());
+        assertEquals(100.0, result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate19() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(1, result.size());
+        assertNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate20() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 or node_cpu_seconds_total{mode=\"idle\"} < 320");
+        assertEquals(1, result.size());
+        assertNotNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate21() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 320");
+        assertEquals(1, result.size());
+        assertNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate22() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(1, result.size());
+        assertNotNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate23() {
+        List<Map<String, Object>> prometheusData1 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        List<Map<String, Object>> prometheusData2 = List.of(
+                new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
+        );
+
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
+        Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(1, result.size());
+        assertNull(result.get(0).get("__value__"));
+    }
+
+    @Test
+    void calculate24() {
+        List<Map<String, Object>> prometheusData = List.of();
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
+        assertEquals(0, result.size());
+    }
+
+    @Test
+    void calculate25() {
+        List<Map<String, Object>> prometheusData = List.of();
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
+        assertEquals(0, result.size());
+    }
+
+    @Test
+    void calculate26() {
+        List<Map<String, Object>> prometheusData = List.of(
+                new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
+        );
+        QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
+        Mockito.when(mockExecutor.support("promql")).thenReturn(true);
+        Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
+        dataSourceService.setExecutors(List.of(mockExecutor));
+
+        List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
+        assertEquals(1, result.size());
+        assertEquals(200.0, result.get(0).get("__value__"));
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@hertzbeat.apache.org
For additional commands, e-mail: notifications-help@hertzbeat.apache.org