xref: /openbmc/telemetry/tests/src/test_trigger.cpp (revision 94f71c5190b64bb47aa34cdce4eb4cca71d36faa)
1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "mocks/json_storage_mock.hpp"
4 #include "mocks/sensor_mock.hpp"
5 #include "mocks/threshold_mock.hpp"
6 #include "mocks/trigger_factory_mock.hpp"
7 #include "mocks/trigger_manager_mock.hpp"
8 #include "params/trigger_params.hpp"
9 #include "trigger.hpp"
10 #include "trigger_manager.hpp"
11 #include "utils/conversion_trigger.hpp"
12 #include "utils/transform.hpp"
13 #include "utils/tstring.hpp"
14 
15 #include <boost/range/combine.hpp>
16 
17 using namespace testing;
18 using namespace std::literals::string_literals;
19 
20 static constexpr size_t expectedTriggerVersion = 1;
21 
22 class TestTrigger : public Test
23 {
24   public:
25     TriggerParams triggerParams;
26     TriggerParams triggerDiscreteParams =
27         TriggerParams()
28             .id("DiscreteTrigger")
29             .name("My Discrete Trigger")
30             .thresholdParams(std::vector<discrete::LabeledThresholdParam>{
31                 discrete::LabeledThresholdParam{
32                     "userId", discrete::Severity::warning,
33                     Milliseconds(10).count(), "15.2"},
34                 discrete::LabeledThresholdParam{
35                     "userId_2", discrete::Severity::critical,
36                     Milliseconds(5).count(), "32.7"},
37             });
38 
39     std::unique_ptr<TriggerManagerMock> triggerManagerMockPtr =
40         std::make_unique<NiceMock<TriggerManagerMock>>();
41     std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr =
42         std::make_unique<NiceMock<TriggerFactoryMock>>();
43     testing::NiceMock<StorageMock> storageMock;
44     std::vector<std::shared_ptr<interfaces::Threshold>> thresholdMocks;
45     std::unique_ptr<Trigger> sut;
46 
47     void SetUp() override
48     {
49         sut = makeTrigger(triggerParams);
50     }
51 
52     static std::vector<LabeledSensorInfo>
53         convertToLabeledSensor(const SensorsInfo& sensorsInfo)
54     {
55         return utils::transform(sensorsInfo, [](const auto& sensorInfo) {
56             const auto& [sensorPath, sensorMetadata] = sensorInfo;
57             return LabeledSensorInfo("service1", sensorPath, sensorMetadata);
58         });
59     }
60 
61     std::unique_ptr<Trigger> makeTrigger(const TriggerParams& params)
62     {
63         thresholdMocks =
64             ThresholdMock::makeThresholds(params.thresholdParams());
65 
66         return std::make_unique<Trigger>(
67             DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(),
68             params.id(), params.name(), params.triggerActions(),
69             std::make_shared<std::vector<std::string>>(
70                 params.reportIds().begin(), params.reportIds().end()),
71             std::vector<std::shared_ptr<interfaces::Threshold>>(thresholdMocks),
72             *triggerManagerMockPtr, storageMock, *triggerFactoryMockPtr,
73             SensorMock::makeSensorMocks(params.sensors()));
74     }
75 
76     static interfaces::JsonStorage::FilePath to_file_path(std::string name)
77     {
78         return interfaces::JsonStorage::FilePath(
79             std::to_string(std::hash<std::string>{}(name)));
80     }
81 
82     template <class T>
83     static T getProperty(const std::string& path, const std::string& property)
84     {
85         return DbusEnvironment::getProperty<T>(path, Trigger::triggerIfaceName,
86                                                property);
87     }
88 
89     template <class T>
90     static boost::system::error_code setProperty(const std::string& path,
91                                                  const std::string& property,
92                                                  const T& newValue)
93     {
94         return DbusEnvironment::setProperty<T>(path, Trigger::triggerIfaceName,
95                                                property, newValue);
96     }
97 
98     boost::system::error_code deleteTrigger(const std::string& path)
99     {
100         std::promise<boost::system::error_code> methodPromise;
101         DbusEnvironment::getBus()->async_method_call(
102             [&methodPromise](boost::system::error_code ec) {
103                 methodPromise.set_value(ec);
104             },
105             DbusEnvironment::serviceName(), path, Trigger::deleteIfaceName,
106             "Delete");
107         return DbusEnvironment::waitForFuture(methodPromise.get_future());
108     }
109 };
110 
111 TEST_F(TestTrigger, checkIfPropertiesAreSet)
112 {
113     EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"),
114                 Eq(triggerParams.name()));
115     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(true));
116     EXPECT_THAT(
117         getProperty<std::vector<std::string>>(sut->getPath(), "TriggerActions"),
118         Eq(utils::transform(
119             triggerParams.triggerActions(),
120             [](const auto& action) { return actionToString(action); })));
121     EXPECT_THAT((getProperty<SensorsInfo>(sut->getPath(), "Sensors")),
122                 Eq(utils::fromLabeledSensorsInfo(triggerParams.sensors())));
123     EXPECT_THAT(
124         getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"),
125         Eq(triggerParams.reportIds()));
126     EXPECT_THAT(
127         getProperty<bool>(sut->getPath(), "Discrete"),
128         Eq(isTriggerThresholdDiscrete(triggerParams.thresholdParams())));
129     EXPECT_THAT(
130         getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"),
131         Eq(std::visit(utils::FromLabeledThresholdParamConversion(),
132                       triggerParams.thresholdParams())));
133 }
134 
135 TEST_F(TestTrigger, setPropertyNameToCorrectValue)
136 {
137     std::string name = "custom name 1234 %^#5";
138     EXPECT_THAT(setProperty(sut->getPath(), "Name", name),
139                 Eq(boost::system::errc::success));
140     EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), Eq(name));
141 }
142 
143 TEST_F(TestTrigger, setPropertyReportNames)
144 {
145     std::vector<std::string> newNames = {"abc", "one", "two"};
146     EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newNames),
147                 Eq(boost::system::errc::success));
148     EXPECT_THAT(
149         getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"),
150         Eq(newNames));
151 }
152 
153 TEST_F(TestTrigger, setPropertySensors)
154 {
155     EXPECT_CALL(*triggerFactoryMockPtr, updateSensors(_, _));
156     for (const auto& threshold : thresholdMocks)
157     {
158         auto thresholdMockPtr =
159             std::dynamic_pointer_cast<NiceMock<ThresholdMock>>(threshold);
160         EXPECT_CALL(*thresholdMockPtr, updateSensors(_));
161     }
162     SensorsInfo newSensors({std::make_pair(
163         sdbusplus::message::object_path("/abc/def"), "metadata")});
164     EXPECT_THAT(setProperty(sut->getPath(), "Sensors", newSensors),
165                 Eq(boost::system::errc::success));
166 }
167 
168 TEST_F(TestTrigger, setPropertyThresholds)
169 {
170     EXPECT_CALL(*triggerFactoryMockPtr, updateThresholds(_, _, _, _, _));
171     TriggerThresholdParams newThresholds =
172         std::vector<discrete::ThresholdParam>(
173             {std::make_tuple("discrete threshold", "OK", 10, "12.3")});
174     EXPECT_THAT(setProperty(sut->getPath(), "Thresholds", newThresholds),
175                 Eq(boost::system::errc::success));
176 }
177 
178 TEST_F(TestTrigger, checkIfNumericCoversionsAreGood)
179 {
180     const auto& labeledParamsBase =
181         std::get<std::vector<numeric::LabeledThresholdParam>>(
182             triggerParams.thresholdParams());
183     const auto paramsToCheck =
184         std::visit(utils::FromLabeledThresholdParamConversion(),
185                    triggerParams.thresholdParams());
186     const auto labeledParamsToCheck =
187         std::get<std::vector<numeric::LabeledThresholdParam>>(std::visit(
188             utils::ToLabeledThresholdParamConversion(), paramsToCheck));
189 
190     for (const auto& [tocheck, base] :
191          boost::combine(labeledParamsToCheck, labeledParamsBase))
192     {
193         EXPECT_THAT(tocheck.at_label<utils::tstring::Type>(),
194                     Eq(base.at_label<utils::tstring::Type>()));
195         EXPECT_THAT(tocheck.at_label<utils::tstring::Direction>(),
196                     Eq(base.at_label<utils::tstring::Direction>()));
197         EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
198                     Eq(base.at_label<utils::tstring::DwellTime>()));
199         EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
200                     Eq(base.at_label<utils::tstring::ThresholdValue>()));
201     }
202 }
203 
204 TEST_F(TestTrigger, checkIfDiscreteCoversionsAreGood)
205 {
206     const auto& labeledParamsBase =
207         std::get<std::vector<discrete::LabeledThresholdParam>>(
208             triggerDiscreteParams.thresholdParams());
209     const auto paramsToCheck =
210         std::visit(utils::FromLabeledThresholdParamConversion(),
211                    triggerDiscreteParams.thresholdParams());
212     const auto labeledParamsToCheck =
213         std::get<std::vector<discrete::LabeledThresholdParam>>(std::visit(
214             utils::ToLabeledThresholdParamConversion(), paramsToCheck));
215 
216     for (const auto& [tocheck, base] :
217          boost::combine(labeledParamsToCheck, labeledParamsBase))
218     {
219         EXPECT_THAT(tocheck.at_label<utils::tstring::UserId>(),
220                     Eq(base.at_label<utils::tstring::UserId>()));
221         EXPECT_THAT(tocheck.at_label<utils::tstring::Severity>(),
222                     Eq(base.at_label<utils::tstring::Severity>()));
223         EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
224                     Eq(base.at_label<utils::tstring::DwellTime>()));
225         EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
226                     Eq(base.at_label<utils::tstring::ThresholdValue>()));
227     }
228 }
229 
230 TEST_F(TestTrigger, deleteTrigger)
231 {
232     EXPECT_CALL(storageMock, remove(to_file_path(sut->getId())));
233     EXPECT_CALL(*triggerManagerMockPtr, removeTrigger(sut.get()));
234     auto ec = deleteTrigger(sut->getPath());
235     EXPECT_THAT(ec, Eq(boost::system::errc::success));
236 }
237 
238 TEST_F(TestTrigger, deletingNonExistingTriggerReturnInvalidRequestDescriptor)
239 {
240     auto ec = deleteTrigger(Trigger::triggerDir + "NonExisting"s);
241     EXPECT_THAT(ec.value(), Eq(EBADR));
242 }
243 
244 TEST_F(TestTrigger, settingPersistencyToFalseRemovesTriggerFromStorage)
245 {
246     EXPECT_CALL(storageMock, remove(to_file_path(sut->getId())));
247 
248     bool persistent = false;
249     EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent),
250                 Eq(boost::system::errc::success));
251     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"),
252                 Eq(persistent));
253 }
254 
255 class TestTriggerErrors : public TestTrigger
256 {
257   public:
258     void SetUp() override
259     {}
260 
261     nlohmann::json storedConfiguration;
262 };
263 
264 TEST_F(TestTriggerErrors, exceptionDuringTriggerStoreDisablesPersistency)
265 {
266     EXPECT_CALL(storageMock, store(_, _))
267         .WillOnce(Throw(std::runtime_error("Generic error!")));
268 
269     sut = makeTrigger(triggerParams);
270 
271     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(false));
272 }
273 
274 TEST_F(TestTriggerErrors, creatingTriggerThrowsExceptionWhenIdIsInvalid)
275 {
276     EXPECT_CALL(storageMock, store(_, _)).Times(0);
277 
278     EXPECT_THROW(makeTrigger(triggerParams.id("inv?lidId")),
279                  sdbusplus::exception::SdBusError);
280 }
281 
282 class TestTriggerStore : public TestTrigger
283 {
284   public:
285     nlohmann::json storedConfiguration;
286     nlohmann::json storedDiscreteConfiguration;
287     std::unique_ptr<Trigger> sutDiscrete;
288 
289     void SetUp() override
290     {
291         ON_CALL(storageMock, store(_, _))
292             .WillByDefault(SaveArg<1>(&storedConfiguration));
293         sut = makeTrigger(triggerParams);
294 
295         ON_CALL(storageMock, store(_, _))
296             .WillByDefault(SaveArg<1>(&storedDiscreteConfiguration));
297         sutDiscrete = makeTrigger(triggerDiscreteParams);
298     }
299 };
300 
301 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerVersion)
302 {
303     ASSERT_THAT(storedConfiguration.at("Version"), Eq(expectedTriggerVersion));
304 }
305 
306 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerId)
307 {
308     ASSERT_THAT(storedConfiguration.at("Id"), Eq(triggerParams.id()));
309 }
310 
311 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerName)
312 {
313     ASSERT_THAT(storedConfiguration.at("Name"), Eq(triggerParams.name()));
314 }
315 
316 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerTriggerActions)
317 {
318     ASSERT_THAT(storedConfiguration.at("TriggerActions"),
319                 Eq(utils::transform(triggerParams.triggerActions(),
320                                     [](const auto& action) {
321                                         return actionToString(action);
322                                     })));
323 }
324 
325 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerReportIds)
326 {
327     ASSERT_THAT(storedConfiguration.at("ReportIds"),
328                 Eq(triggerParams.reportIds()));
329 }
330 
331 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerSensors)
332 {
333     nlohmann::json expectedItem;
334     expectedItem["service"] = "service1";
335     expectedItem["path"] = "/xyz/openbmc_project/sensors/temperature/BMC_Temp";
336     expectedItem["metadata"] = "metadata1";
337 
338     ASSERT_THAT(storedConfiguration.at("Sensors"), ElementsAre(expectedItem));
339 }
340 
341 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerThresholdParams)
342 {
343     nlohmann::json expectedItem0;
344     expectedItem0["type"] = 0;
345     expectedItem0["dwellTime"] = 10;
346     expectedItem0["direction"] = 1;
347     expectedItem0["thresholdValue"] = 0.5;
348 
349     nlohmann::json expectedItem1;
350     expectedItem1["type"] = 3;
351     expectedItem1["dwellTime"] = 10;
352     expectedItem1["direction"] = 2;
353     expectedItem1["thresholdValue"] = 90.2;
354 
355     ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0));
356     ASSERT_THAT(storedConfiguration.at("ThresholdParams"),
357                 ElementsAre(expectedItem0, expectedItem1));
358 }
359 
360 TEST_F(TestTriggerStore,
361        settingPersistencyToTrueStoresDiscreteTriggerThresholdParams)
362 {
363     nlohmann::json expectedItem0;
364     expectedItem0["userId"] = "userId";
365     expectedItem0["severity"] = discrete::Severity::warning;
366     expectedItem0["dwellTime"] = 10;
367     expectedItem0["thresholdValue"] = "15.2";
368 
369     nlohmann::json expectedItem1;
370     expectedItem1["userId"] = "userId_2";
371     expectedItem1["severity"] = discrete::Severity::critical;
372     expectedItem1["dwellTime"] = 5;
373     expectedItem1["thresholdValue"] = "32.7";
374 
375     ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParamsDiscriminator"),
376                 Eq(1));
377     ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParams"),
378                 ElementsAre(expectedItem0, expectedItem1));
379 }
380