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