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