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             .isDiscrete(true)
27             .thresholdParams(std::vector<discrete::LabeledThresholdParam>{
28                 discrete::LabeledThresholdParam{
29                     "userId", discrete::Severity::warning,
30                     std::chrono::milliseconds(10).count(), 15.2},
31                 discrete::LabeledThresholdParam{
32                     "userId_2", discrete::Severity::critical,
33                     std::chrono::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.name(), params.isDiscrete(), params.logToJournal(),
60             params.logToRedfish(), params.updateReport(), params.reportNames(),
61             params.sensors(), params.thresholdParams(),
62             std::vector<std::shared_ptr<interfaces::Threshold>>{},
63             *triggerManagerMockPtr, storageMock);
64     }
65 
66     static interfaces::JsonStorage::FilePath to_file_path(std::string name)
67     {
68         return interfaces::JsonStorage::FilePath(
69             std::to_string(std::hash<std::string>{}(name)));
70     }
71 
72     template <class T>
73     static T getProperty(const std::string& path, const std::string& property)
74     {
75         auto propertyPromise = std::promise<T>();
76         auto propertyFuture = propertyPromise.get_future();
77         sdbusplus::asio::getProperty<T>(
78             *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path,
79             Trigger::triggerIfaceName, property,
80             [&propertyPromise](const boost::system::error_code& ec, T t) {
81                 if (ec)
82                 {
83                     utils::setException(propertyPromise, "GetProperty failed");
84                     return;
85                 }
86                 propertyPromise.set_value(t);
87             });
88         return DbusEnvironment::waitForFuture(std::move(propertyFuture));
89     }
90 
91     template <class T>
92     static boost::system::error_code setProperty(const std::string& path,
93                                                  const std::string& property,
94                                                  const T& newValue)
95     {
96         auto setPromise = std::promise<boost::system::error_code>();
97         auto setFuture = setPromise.get_future();
98 
99         sdbusplus::asio::setProperty(
100             *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path,
101             Trigger::triggerIfaceName, property, std::move(newValue),
102             [setPromise =
103                  std::move(setPromise)](boost::system::error_code ec) mutable {
104                 setPromise.set_value(ec);
105             });
106         return DbusEnvironment::waitForFuture(std::move(setFuture));
107     }
108 
109     boost::system::error_code deleteTrigger(const std::string& path)
110     {
111         std::promise<boost::system::error_code> methodPromise;
112         DbusEnvironment::getBus()->async_method_call(
113             [&methodPromise](boost::system::error_code ec) {
114                 methodPromise.set_value(ec);
115             },
116             DbusEnvironment::serviceName(), path, Trigger::deleteIfaceName,
117             "Delete");
118         return DbusEnvironment::waitForFuture(methodPromise.get_future());
119     }
120 };
121 
122 TEST_F(TestTrigger, checkIfPropertiesAreSet)
123 {
124     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(true));
125     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Discrete"),
126                 Eq(triggerParams.isDiscrete()));
127     EXPECT_THAT(getProperty<bool>(sut->getPath(), "LogToJournal"),
128                 Eq(triggerParams.logToJournal()));
129     EXPECT_THAT(getProperty<bool>(sut->getPath(), "LogToRedfish"),
130                 Eq(triggerParams.logToRedfish()));
131     EXPECT_THAT(getProperty<bool>(sut->getPath(), "UpdateReport"),
132                 Eq(triggerParams.updateReport()));
133     EXPECT_THAT((getProperty<SensorsInfo>(sut->getPath(), "Sensors")),
134                 Eq(utils::fromLabeledSensorsInfo(triggerParams.sensors())));
135     EXPECT_THAT(
136         getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"),
137         Eq(triggerParams.reportNames()));
138     EXPECT_THAT(
139         getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"),
140         Eq(std::visit(utils::FromLabeledThresholdParamConversion(),
141                       triggerParams.thresholdParams())));
142 }
143 
144 TEST_F(TestTrigger, checkIfNumericCoversionsAreGood)
145 {
146     const auto& labeledParamsBase =
147         std::get<std::vector<numeric::LabeledThresholdParam>>(
148             triggerParams.thresholdParams());
149     const auto paramsToCheck =
150         std::visit(utils::FromLabeledThresholdParamConversion(),
151                    triggerParams.thresholdParams());
152     const auto labeledParamsToCheck =
153         std::get<std::vector<numeric::LabeledThresholdParam>>(std::visit(
154             utils::ToLabeledThresholdParamConversion(), paramsToCheck));
155 
156     for (const auto& [tocheck, base] :
157          boost::combine(labeledParamsToCheck, labeledParamsBase))
158     {
159         EXPECT_THAT(tocheck.at_label<utils::tstring::Type>(),
160                     Eq(base.at_label<utils::tstring::Type>()));
161         EXPECT_THAT(tocheck.at_label<utils::tstring::Direction>(),
162                     Eq(base.at_label<utils::tstring::Direction>()));
163         EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
164                     Eq(base.at_label<utils::tstring::DwellTime>()));
165         EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
166                     Eq(base.at_label<utils::tstring::ThresholdValue>()));
167     }
168 }
169 
170 TEST_F(TestTrigger, checkIfDiscreteCoversionsAreGood)
171 {
172     const auto& labeledParamsBase =
173         std::get<std::vector<discrete::LabeledThresholdParam>>(
174             triggerDiscreteParams.thresholdParams());
175     const auto paramsToCheck =
176         std::visit(utils::FromLabeledThresholdParamConversion(),
177                    triggerDiscreteParams.thresholdParams());
178     const auto labeledParamsToCheck =
179         std::get<std::vector<discrete::LabeledThresholdParam>>(std::visit(
180             utils::ToLabeledThresholdParamConversion(), paramsToCheck));
181 
182     for (const auto& [tocheck, base] :
183          boost::combine(labeledParamsToCheck, labeledParamsBase))
184     {
185         EXPECT_THAT(tocheck.at_label<utils::tstring::UserId>(),
186                     Eq(base.at_label<utils::tstring::UserId>()));
187         EXPECT_THAT(tocheck.at_label<utils::tstring::Severity>(),
188                     Eq(base.at_label<utils::tstring::Severity>()));
189         EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
190                     Eq(base.at_label<utils::tstring::DwellTime>()));
191         EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
192                     Eq(base.at_label<utils::tstring::ThresholdValue>()));
193     }
194 }
195 
196 TEST_F(TestTrigger, deleteTrigger)
197 {
198     EXPECT_CALL(storageMock, remove(to_file_path(sut->getName())));
199     EXPECT_CALL(*triggerManagerMockPtr, removeTrigger(sut.get()));
200     auto ec = deleteTrigger(sut->getPath());
201     EXPECT_THAT(ec, Eq(boost::system::errc::success));
202 }
203 
204 TEST_F(TestTrigger, deletingNonExistingTriggerReturnInvalidRequestDescriptor)
205 {
206     auto ec = deleteTrigger(Trigger::triggerDir + "NonExisting"s);
207     EXPECT_THAT(ec.value(), Eq(EBADR));
208 }
209 
210 TEST_F(TestTrigger, settingPersistencyToFalseRemovesTriggerFromStorage)
211 {
212     EXPECT_CALL(storageMock, remove(to_file_path(sut->getName())));
213 
214     bool persistent = false;
215     EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent),
216                 Eq(boost::system::errc::success));
217     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"),
218                 Eq(persistent));
219 }
220 
221 class TestTriggerErrors : public TestTrigger
222 {
223   public:
224     void SetUp() override
225     {}
226 
227     nlohmann::json storedConfiguration;
228 };
229 
230 TEST_F(TestTriggerErrors, exceptionDuringTriggerStoreDisablesPersistency)
231 {
232     EXPECT_CALL(storageMock, store(_, _))
233         .WillOnce(Throw(std::runtime_error("Generic error!")));
234 
235     sut = makeTrigger(triggerParams);
236 
237     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(false));
238 }
239 
240 TEST_F(TestTriggerErrors, creatingTriggerThrowsExceptionWhenNameIsInvalid)
241 {
242     EXPECT_CALL(storageMock, store(_, _)).Times(0);
243 
244     EXPECT_THROW(makeTrigger(triggerParams.name("inv?lidName")),
245                  sdbusplus::exception::SdBusError);
246 }
247 
248 class TestTriggerStore : public TestTrigger
249 {
250   public:
251     nlohmann::json storedConfiguration;
252     nlohmann::json storedDiscreteConfiguration;
253     std::unique_ptr<Trigger> sutDiscrete;
254 
255     void SetUp() override
256     {
257         ON_CALL(storageMock, store(_, _))
258             .WillByDefault(SaveArg<1>(&storedConfiguration));
259         sut = makeTrigger(triggerParams);
260 
261         ON_CALL(storageMock, store(_, _))
262             .WillByDefault(SaveArg<1>(&storedDiscreteConfiguration));
263         sutDiscrete = makeTrigger(triggerDiscreteParams);
264     }
265 };
266 
267 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerVersion)
268 {
269     ASSERT_THAT(storedConfiguration.at("Version"), Eq(expectedTriggerVersion));
270 }
271 
272 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerName)
273 {
274     ASSERT_THAT(storedConfiguration.at("Name"), Eq(triggerParams.name()));
275 }
276 
277 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerIsDiscrete)
278 {
279     ASSERT_THAT(storedConfiguration.at("IsDiscrete"),
280                 Eq(triggerParams.isDiscrete()));
281 }
282 
283 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerLogToJournal)
284 {
285     ASSERT_THAT(storedConfiguration.at("LogToJournal"),
286                 Eq(triggerParams.logToRedfish()));
287 }
288 
289 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerLogToRedfish)
290 {
291     ASSERT_THAT(storedConfiguration.at("LogToRedfish"),
292                 Eq(triggerParams.logToRedfish()));
293 }
294 
295 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerUpdateReport)
296 {
297     ASSERT_THAT(storedConfiguration.at("UpdateReport"),
298                 Eq(triggerParams.updateReport()));
299 }
300 
301 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerReportNames)
302 {
303     ASSERT_THAT(storedConfiguration.at("ReportNames"),
304                 Eq(triggerParams.reportNames()));
305 }
306 
307 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerSensors)
308 {
309     nlohmann::json expectedItem;
310     expectedItem["service"] = "service1";
311     expectedItem["sensorPath"] =
312         "/xyz/openbmc_project/sensors/temperature/BMC_Temp";
313     expectedItem["sensorMetadata"] = "metadata1";
314 
315     ASSERT_THAT(storedConfiguration.at("Sensors"), ElementsAre(expectedItem));
316 }
317 
318 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerThresholdParams)
319 {
320     nlohmann::json expectedItem0;
321     expectedItem0["type"] = 0;
322     expectedItem0["dwellTime"] = 10;
323     expectedItem0["direction"] = 1;
324     expectedItem0["thresholdValue"] = 0.5;
325 
326     nlohmann::json expectedItem1;
327     expectedItem1["type"] = 3;
328     expectedItem1["dwellTime"] = 10;
329     expectedItem1["direction"] = 2;
330     expectedItem1["thresholdValue"] = 90.2;
331 
332     ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0));
333     ASSERT_THAT(storedConfiguration.at("ThresholdParams"),
334                 ElementsAre(expectedItem0, expectedItem1));
335 }
336 
337 TEST_F(TestTriggerStore,
338        settingPersistencyToTrueStoresDiscreteTriggerThresholdParams)
339 {
340     nlohmann::json expectedItem0;
341     expectedItem0["userId"] = "userId";
342     expectedItem0["severity"] = discrete::Severity::warning;
343     expectedItem0["dwellTime"] = 10;
344     expectedItem0["thresholdValue"] = 15.2;
345 
346     nlohmann::json expectedItem1;
347     expectedItem1["userId"] = "userId_2";
348     expectedItem1["severity"] = discrete::Severity::critical;
349     expectedItem1["dwellTime"] = 5;
350     expectedItem1["thresholdValue"] = 32.7;
351 
352     ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParamsDiscriminator"),
353                 Eq(1));
354     ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParams"),
355                 ElementsAre(expectedItem0, expectedItem1));
356 }
357