xref: /openbmc/telemetry/tests/src/test_trigger_manager.cpp (revision 94f71c5190b64bb47aa34cdce4eb4cca71d36faa)
1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "mocks/json_storage_mock.hpp"
4 #include "mocks/trigger_factory_mock.hpp"
5 #include "mocks/trigger_mock.hpp"
6 #include "params/trigger_params.hpp"
7 #include "trigger.hpp"
8 #include "trigger_manager.hpp"
9 #include "utils/conversion_trigger.hpp"
10 #include "utils/transform.hpp"
11 
12 using namespace testing;
13 
14 class TestTriggerManager : public Test
15 {
16   public:
17     std::pair<boost::system::error_code, std::string>
18         addTrigger(const TriggerParams& params)
19     {
20         const auto sensorInfos =
21             utils::fromLabeledSensorsInfo(params.sensors());
22 
23         std::promise<std::pair<boost::system::error_code, std::string>>
24             addTriggerPromise;
25         DbusEnvironment::getBus()->async_method_call(
26             [&addTriggerPromise](boost::system::error_code ec,
27                                  const std::string& path) {
28                 addTriggerPromise.set_value({ec, path});
29             },
30             DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath,
31             TriggerManager::triggerManagerIfaceName, "AddTrigger", params.id(),
32             params.name(),
33             utils::transform(
34                 params.triggerActions(),
35                 [](const auto& action) { return actionToString(action); }),
36             sensorInfos, params.reportIds(),
37             std::visit(utils::FromLabeledThresholdParamConversion(),
38                        params.thresholdParams()));
39         return DbusEnvironment::waitForFuture(addTriggerPromise.get_future());
40     }
41 
42     std::unique_ptr<TriggerManager> makeTriggerManager()
43     {
44         return std::make_unique<TriggerManager>(
45             std::move(triggerFactoryMockPtr), std::move(storageMockPtr),
46             DbusEnvironment::getObjServer());
47     }
48 
49     void SetUp() override
50     {
51         sut = makeTriggerManager();
52     }
53 
54     std::unique_ptr<StorageMock> storageMockPtr =
55         std::make_unique<NiceMock<StorageMock>>();
56     StorageMock& storageMock = *storageMockPtr;
57     std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr =
58         std::make_unique<NiceMock<TriggerFactoryMock>>();
59     TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr;
60     std::unique_ptr<TriggerMock> triggerMockPtr =
61         std::make_unique<NiceMock<TriggerMock>>(TriggerParams().id());
62     TriggerMock& triggerMock = *triggerMockPtr;
63     std::unique_ptr<TriggerManager> sut;
64     MockFunction<void(std::string)> checkPoint;
65 };
66 
67 TEST_F(TestTriggerManager, addTrigger)
68 {
69     triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
70         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
71 
72     auto [ec, path] = addTrigger(TriggerParams());
73     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
74     EXPECT_THAT(path, Eq(triggerMock.getPath()));
75 }
76 
77 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds)
78 {
79     TriggerParams triggerParamsDiscrete;
80     auto thresholds = std::vector<discrete::LabeledThresholdParam>{
81         {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"},
82         {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"},
83         {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}};
84 
85     triggerParamsDiscrete.thresholdParams(thresholds);
86 
87     auto [ec, path] = addTrigger(triggerParamsDiscrete);
88     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
89     EXPECT_THAT(path, Eq(triggerMock.getPath()));
90 }
91 
92 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds)
93 {
94     TriggerParams triggerParamsDiscrete;
95     auto thresholds = std::vector<discrete::LabeledThresholdParam>();
96 
97     triggerParamsDiscrete.thresholdParams(thresholds);
98 
99     auto [ec, path] = addTrigger(triggerParamsDiscrete);
100     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
101     EXPECT_THAT(path, Eq(triggerMock.getPath()));
102 }
103 
104 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice)
105 {
106     triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
107         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
108 
109     addTrigger(TriggerParams());
110 
111     auto [ec, path] = addTrigger(TriggerParams());
112     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
113     EXPECT_THAT(path, Eq(std::string()));
114 }
115 
116 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithInvalidId)
117 {
118     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
119         .Times(0);
120 
121     auto [ec, path] = addTrigger(TriggerParams().id("not valid?"));
122     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
123     EXPECT_THAT(path, Eq(std::string()));
124 }
125 
126 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName)
127 {
128     triggerFactoryMock
129         .expectMake(TriggerParams()
130                         .id(TriggerManager::triggerNameDefault)
131                         .name(TriggerManager::triggerNameDefault),
132                     Ref(*sut), Ref(storageMock))
133         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
134 
135     auto [ec, path] = addTrigger(TriggerParams().id("").name(""));
136     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
137     EXPECT_THAT(path, Not(Eq("")));
138 }
139 
140 TEST_F(TestTriggerManager, addTriggerWithPrefixId)
141 {
142     triggerFactoryMock
143         .expectMake(TriggerParams()
144                         .id("TelemetryService/HackyName")
145                         .name("Hacky/Name!@#$"),
146                     Ref(*sut), Ref(storageMock))
147         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
148 
149     auto [ec, path] = addTrigger(
150         TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$"));
151     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
152     EXPECT_THAT(path, Not(Eq("")));
153 }
154 
155 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice)
156 {
157     addTrigger(TriggerParams().id(""));
158 
159     auto [ec, path] = addTrigger(TriggerParams().id(""));
160     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
161     EXPECT_THAT(path, Not(Eq("")));
162 }
163 
164 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice)
165 {
166     addTrigger(TriggerParams().id("").name(
167         std::string(2 * TriggerManager::maxTriggerIdLength, 'z')));
168 
169     auto [ec, path] = addTrigger(TriggerParams().id("").name(
170         std::string(2 * TriggerManager::maxTriggerIdLength, 'z')));
171     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
172     EXPECT_THAT(path, Not(Eq("")));
173 }
174 
175 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId)
176 {
177     auto triggerId = std::string(TriggerManager::maxTriggerIdLength, 'z');
178     auto triggerParams = TriggerParams().id(triggerId);
179 
180     triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock))
181         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
182 
183     auto [ec, path] = addTrigger(triggerParams);
184 
185     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
186     EXPECT_THAT(path, Eq(triggerMock.getPath()));
187 }
188 
189 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId)
190 {
191     auto triggerId = std::string(TriggerManager::maxTriggerIdLength + 1, 'z');
192     auto triggerParams = TriggerParams().id(triggerId);
193 
194     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
195         .Times(0);
196 
197     auto [ec, path] = addTrigger(triggerParams);
198 
199     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
200     EXPECT_THAT(path, Eq(std::string()));
201 }
202 
203 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached)
204 {
205     auto triggerParams = TriggerParams();
206 
207     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
208         .Times(TriggerManager::maxTriggers);
209 
210     for (size_t i = 0; i < TriggerManager::maxTriggers; i++)
211     {
212         triggerParams.id(TriggerParams().id() + std::to_string(i));
213 
214         auto [ec, path] = addTrigger(triggerParams);
215         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
216     }
217 
218     triggerParams.id(TriggerParams().id() +
219                      std::to_string(TriggerManager::maxTriggers));
220     auto [ec, path] = addTrigger(triggerParams);
221     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
222     EXPECT_THAT(path, Eq(std::string()));
223 }
224 
225 TEST_F(TestTriggerManager, removeTrigger)
226 {
227     {
228         InSequence seq;
229         triggerFactoryMock
230             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
231             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
232         EXPECT_CALL(triggerMock, Die());
233         EXPECT_CALL(checkPoint, Call("end"));
234     }
235 
236     addTrigger(TriggerParams());
237     sut->removeTrigger(&triggerMock);
238     checkPoint.Call("end");
239 }
240 
241 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect)
242 {
243     {
244         InSequence seq;
245         EXPECT_CALL(checkPoint, Call("end"));
246         EXPECT_CALL(triggerMock, Die());
247     }
248 
249     sut->removeTrigger(&triggerMock);
250     checkPoint.Call("end");
251 }
252 
253 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect)
254 {
255     {
256         InSequence seq;
257         triggerFactoryMock
258             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
259             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
260         EXPECT_CALL(triggerMock, Die());
261         EXPECT_CALL(checkPoint, Call("end"));
262     }
263 
264     addTrigger(TriggerParams());
265     sut->removeTrigger(&triggerMock);
266     sut->removeTrigger(&triggerMock);
267     checkPoint.Call("end");
268 }
269 
270 class TestTriggerManagerStorage : public TestTriggerManager
271 {
272   public:
273     using FilePath = interfaces::JsonStorage::FilePath;
274     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
275 
276     void SetUp() override
277     {
278         ON_CALL(storageMock, list())
279             .WillByDefault(Return(std::vector<FilePath>{
280                 {FilePath("trigger1")}, {FilePath("trigger2")}}));
281 
282         ON_CALL(storageMock, load(FilePath("trigger1")))
283             .WillByDefault(InvokeWithoutArgs([this] { return data1; }));
284 
285         data2["Id"] = "Trigger2";
286         data2["Name"] = "Second Trigger";
287         ON_CALL(storageMock, load(FilePath("trigger2")))
288             .WillByDefault(InvokeWithoutArgs([this] { return data2; }));
289     }
290 
291     nlohmann::json data1 = nlohmann::json{
292         {"Version", Trigger::triggerVersion},
293         {"Id", TriggerParams().id()},
294         {"Name", TriggerParams().name()},
295         {"ThresholdParamsDiscriminator",
296          TriggerParams().thresholdParams().index()},
297         {"TriggerActions", utils::transform(TriggerParams().triggerActions(),
298                                             [](const auto& action) {
299                                                 return actionToString(action);
300                                             })},
301         {"ThresholdParams", utils::labeledThresholdParamsToJson(
302                                 TriggerParams().thresholdParams())},
303         {"ReportIds", TriggerParams().reportIds()},
304         {"Sensors", TriggerParams().sensors()}};
305 
306     nlohmann::json data2 = data1;
307 };
308 
309 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage)
310 {
311     triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock));
312     triggerFactoryMock.expectMake(
313         TriggerParams().id("Trigger2").name("Second Trigger"), _,
314         Ref(storageMock));
315     EXPECT_CALL(storageMock, remove(_)).Times(0);
316 
317     sut = makeTriggerManager();
318 }
319 
320 TEST_F(TestTriggerManagerStorage,
321        triggerManagerCtorRemoveDiscreteTriggerFromStorage)
322 {
323     LabeledTriggerThresholdParams thresholdParams =
324         std::vector<discrete::LabeledThresholdParam>{
325             {"userId1", discrete::Severity::warning, 15, "10.0"},
326             {"userId2", discrete::Severity::critical, 5, "20.0"}};
327 
328     data1["ThresholdParamsDiscriminator"] = thresholdParams.index();
329 
330     data1["ThresholdParams"] =
331         utils::labeledThresholdParamsToJson(thresholdParams);
332 
333     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
334 
335     sut = makeTriggerManager();
336 }
337 
338 TEST_F(TestTriggerManagerStorage,
339        triggerManagerCtorRemoveDiscreteTriggerFromStorage2)
340 {
341     data1["IsDiscrete"] = true;
342 
343     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
344 
345     sut = makeTriggerManager();
346 }
347 
348 TEST_F(TestTriggerManagerStorage,
349        triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage)
350 {
351     data1["Version"] = Trigger::triggerVersion - 1;
352 
353     triggerFactoryMock.expectMake(
354         TriggerParams().id("Trigger2").name("Second Trigger"), _,
355         Ref(storageMock));
356     EXPECT_CALL(storageMock, remove(FilePath("trigger1")));
357 
358     sut = makeTriggerManager();
359 }
360