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