tests: test alarms and led coordination through sysrepo-ietf-alarms

Depends-On: https://gerrit.cesnet.cz/c/CzechLight/sysrepo-ietf-alarms/+/6052
Change-Id: If964baf29774dc1aa15f5e87887b269d832f3e5a
diff --git a/tests/sysrepo_ietf-alarms.cpp b/tests/sysrepo_ietf-alarms.cpp
new file mode 100644
index 0000000..a3f6b15
--- /dev/null
+++ b/tests/sysrepo_ietf-alarms.cpp
@@ -0,0 +1,87 @@
+#include "trompeloeil_doctest.h"
+#include <sysrepo-cpp/Enum.hpp>
+#include <trompeloeil.hpp>
+#include "dbus-helpers/dbus_rauc_server.h"
+#include "health/State.h"
+#include "health/alarms/SystemdUnits.h"
+#include "health/outputs/AlarmsOutputs.h"
+#include "pretty_printers.h"
+#include "test_log_setup.h"
+#include "test_sysrepo_helpers.h"
+#include "tests/dbus-helpers/dbus_systemd_server.h"
+#include "utils/io.h"
+
+using namespace std::literals;
+
+struct FakeLedCallback {
+    MAKE_CONST_MOCK1(call, void(velia::health::State));
+};
+
+#define EXPECT_COLOUR(STATE) REQUIRE_CALL(fakeLeds, call(STATE)).IN_SEQUENCE(seq1)
+
+TEST_CASE("Test raising alarms lighting LEDs with real sysrepo-ietf-alarmsd server")
+{
+    trompeloeil::sequence seq1;
+
+    TEST_SYSREPO_INIT_LOGS;
+    TEST_SYSREPO_INIT_CLIENT;
+
+    {
+        TEST_SYSREPO_INIT;
+        auto srSessLED = srConn.sessionStart();
+
+        // Create and setup separate connections for both client and server to simulate real-world (systemd server and our client)
+        auto clientConnection = sdbus::createSessionBusConnection();
+        auto serverConnection = sdbus::createSessionBusConnection();
+        clientConnection->enterEventLoopAsync();
+        serverConnection->enterEventLoopAsync();
+        DbusSystemdServer systemdServer(*serverConnection);
+
+        client.switchDatastore(sysrepo::Datastore::Operational);
+        srSess.switchDatastore(sysrepo::Datastore::Operational);
+        srSessLED.switchDatastore(sysrepo::Datastore::Operational);
+
+        systemdServer.createUnit(*serverConnection, "unit1.service", "/org/freedesktop/systemd1/unit/unit1", "active", "running");
+        systemdServer.createUnit(*serverConnection, "unit2.service", "/org/freedesktop/systemd1/unit/unit2", "activating", "auto-restart");
+        systemdServer.createUnit(*serverConnection, "unit3.service", "/org/freedesktop/systemd1/unit/unit3", "failed", "failed");
+
+        FakeLedCallback fakeLeds;
+        velia::health::SystemdUnits systemdAlarms(srSess, *clientConnection, serverConnection->getUniqueName(), "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "org.freedesktop.systemd1.Unit");
+
+        std::this_thread::sleep_for(100ms); // let's wait for some time so the alarm-inventory is populated
+
+        REQUIRE(dataFromSysrepo(client, "/ietf-alarms:alarms/alarm-inventory", sysrepo::Datastore::Operational) == std::map<std::string, std::string>{
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']", ""},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/alarm-type-id", "velia-alarms:systemd-unit-failure"},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/alarm-type-qualifier", ""},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/description", "The systemd service is considered in failed state."},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/resource[1]", "unit1.service"},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/resource[2]", "unit2.service"},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/resource[3]", "unit3.service"},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/severity-level[1]", "critical"},
+                    {"/alarm-type[alarm-type-id='velia-alarms:systemd-unit-failure'][alarm-type-qualifier='']/will-clear", "true"},
+                });
+
+        EXPECT_COLOUR(velia::health::State::ERROR);
+        velia::health::AlarmsOutputs alarms(srSessLED, {[&](velia::health::State state) { fakeLeds.call(state); }});
+
+        EXPECT_COLOUR(velia::health::State::ERROR);
+        systemdServer.changeUnitState("/org/freedesktop/systemd1/unit/unit2", "active", "running");
+
+        EXPECT_COLOUR(velia::health::State::ERROR);
+        systemdServer.changeUnitState("/org/freedesktop/systemd1/unit/unit1", "activating", "auto-restart");
+
+        EXPECT_COLOUR(velia::health::State::ERROR);
+        systemdServer.changeUnitState("/org/freedesktop/systemd1/unit/unit3", "active", "running");
+
+        EXPECT_COLOUR(velia::health::State::OK);
+        systemdServer.changeUnitState("/org/freedesktop/systemd1/unit/unit1", "active", "running");
+
+        EXPECT_COLOUR(velia::health::State::ERROR);
+        systemdServer.changeUnitState("/org/freedesktop/systemd1/unit/unit3", "failed", "failed");
+
+        waitForCompletionAndBitMore(seq1);
+    }
+
+    REQUIRE(!client.getData("/ietf-alarms:alarms/alarm-inventory/*"));
+}
diff --git a/tests/sysrepo_sysrepo-ietf-alarms_control.sh b/tests/sysrepo_sysrepo-ietf-alarms_control.sh
new file mode 100755
index 0000000..c839b98
--- /dev/null
+++ b/tests/sysrepo_sysrepo-ietf-alarms_control.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+PIDFILE="./test-alarms.pid"
+
+set -x
+
+stop() {
+    [[ ! -r "$PIDFILE" ]] && return 0 # no pidfile
+
+    PID="$(cat "$PIDFILE")"
+
+    if ps --pid "$PID" >/dev/null; then
+      kill -15 -- "$PID" 2>/dev/null  # please terminate
+      sleep 0.5
+      while ps --pid "$PID" >/dev/null; do
+        kill -9 -- "$PID" 2>/dev/null # shots fired
+      done
+    fi
+    echo "" > "$PIDFILE"
+}
+
+# $1 ... path to daemon
+start() {
+    rm -f "$PIDFILE" # in case these files already exist
+
+    if [[ ! -x "$1" ]]; then
+        echo "$1 is not executable" >&2
+        return 1
+    fi
+
+    $1 --sysrepo-log-level=5 1>$2 2>&1 &
+    PID="$!"
+    echo "$PID" > $PIDFILE
+
+    # wait for init
+    sleep 2
+
+    echo "Started $1 (pid=$PID)" >&2
+}
+
+help() {
+    echo "Usage:" >&2
+    echo "  $1 start PATH_TO_SYSREPO_IETF_ALARMSD LOGFILE" >&2
+    echo "  $1 stop" >&2
+}
+
+if [[ "$1" == "start" && $# == 3 ]]; then
+    stop
+    start "$2" "$3"
+elif [[ "$1" == "stop" && $# == 1 ]]; then
+    stop
+else
+    help
+    exit 1
+fi