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, DISABLED_failToAddTriggerWithDuplicatesInReportsIds)
127 {
128     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
129         .Times(0);
130 
131     auto [ec, path] = addTrigger(
132         TriggerParams().reportIds({"trigger1", "trigger2", "trigger1"}));
133     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
134     EXPECT_THAT(path, Eq(std::string()));
135 }
136 
137 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName)
138 {
139     triggerFactoryMock
140         .expectMake(TriggerParams()
141                         .id(TriggerManager::triggerNameDefault)
142                         .name(TriggerManager::triggerNameDefault),
143                     Ref(*sut), Ref(storageMock))
144         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
145 
146     auto [ec, path] = addTrigger(TriggerParams().id("").name(""));
147     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
148     EXPECT_THAT(path, Not(Eq("")));
149 }
150 
151 TEST_F(TestTriggerManager, addTriggerWithPrefixId)
152 {
153     triggerFactoryMock
154         .expectMake(TriggerParams()
155                         .id("TelemetryService/HackyName")
156                         .name("Hacky/Name!@#$"),
157                     Ref(*sut), Ref(storageMock))
158         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
159 
160     auto [ec, path] = addTrigger(
161         TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$"));
162     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
163     EXPECT_THAT(path, Not(Eq("")));
164 }
165 
166 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice)
167 {
168     addTrigger(TriggerParams().id(""));
169 
170     auto [ec, path] = addTrigger(TriggerParams().id(""));
171     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
172     EXPECT_THAT(path, Not(Eq("")));
173 }
174 
175 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice)
176 {
177     addTrigger(TriggerParams().id("").name(
178         std::string(2 * TriggerManager::maxTriggerIdLength, 'z')));
179 
180     auto [ec, path] = addTrigger(TriggerParams().id("").name(
181         std::string(2 * TriggerManager::maxTriggerIdLength, 'z')));
182     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
183     EXPECT_THAT(path, Not(Eq("")));
184 }
185 
186 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId)
187 {
188     auto triggerId = std::string(TriggerManager::maxTriggerIdLength, 'z');
189     auto triggerParams = TriggerParams().id(triggerId);
190 
191     triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock))
192         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
193 
194     auto [ec, path] = addTrigger(triggerParams);
195 
196     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
197     EXPECT_THAT(path, Eq(triggerMock.getPath()));
198 }
199 
200 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId)
201 {
202     auto triggerId = std::string(TriggerManager::maxTriggerIdLength + 1, 'z');
203     auto triggerParams = TriggerParams().id(triggerId);
204 
205     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
206         .Times(0);
207 
208     auto [ec, path] = addTrigger(triggerParams);
209 
210     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
211     EXPECT_THAT(path, Eq(std::string()));
212 }
213 
214 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached)
215 {
216     auto triggerParams = TriggerParams();
217 
218     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
219         .Times(TriggerManager::maxTriggers);
220 
221     for (size_t i = 0; i < TriggerManager::maxTriggers; i++)
222     {
223         triggerParams.id(TriggerParams().id() + std::to_string(i));
224 
225         auto [ec, path] = addTrigger(triggerParams);
226         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
227     }
228 
229     triggerParams.id(TriggerParams().id() +
230                      std::to_string(TriggerManager::maxTriggers));
231     auto [ec, path] = addTrigger(triggerParams);
232     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
233     EXPECT_THAT(path, Eq(std::string()));
234 }
235 
236 TEST_F(TestTriggerManager, removeTrigger)
237 {
238     {
239         InSequence seq;
240         triggerFactoryMock
241             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
242             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
243         EXPECT_CALL(triggerMock, Die());
244         EXPECT_CALL(checkPoint, Call("end"));
245     }
246 
247     addTrigger(TriggerParams());
248     sut->removeTrigger(&triggerMock);
249     checkPoint.Call("end");
250 }
251 
252 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect)
253 {
254     {
255         InSequence seq;
256         EXPECT_CALL(checkPoint, Call("end"));
257         EXPECT_CALL(triggerMock, Die());
258     }
259 
260     sut->removeTrigger(&triggerMock);
261     checkPoint.Call("end");
262 }
263 
264 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect)
265 {
266     {
267         InSequence seq;
268         triggerFactoryMock
269             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
270             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
271         EXPECT_CALL(triggerMock, Die());
272         EXPECT_CALL(checkPoint, Call("end"));
273     }
274 
275     addTrigger(TriggerParams());
276     sut->removeTrigger(&triggerMock);
277     sut->removeTrigger(&triggerMock);
278     checkPoint.Call("end");
279 }
280 class TestTriggerManagerStorage : public TestTriggerManager
281 {
282   public:
283     using FilePath = interfaces::JsonStorage::FilePath;
284     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
285 
286     void SetUp() override
287     {
288         ON_CALL(storageMock, list())
289             .WillByDefault(Return(std::vector<FilePath>{
290                 {FilePath("trigger1")}, {FilePath("trigger2")}}));
291 
292         ON_CALL(storageMock, load(FilePath("trigger1")))
293             .WillByDefault(InvokeWithoutArgs([this] { return data1; }));
294 
295         data2["Id"] = "Trigger2";
296         data2["Name"] = "Second Trigger";
297         ON_CALL(storageMock, load(FilePath("trigger2")))
298             .WillByDefault(InvokeWithoutArgs([this] { return data2; }));
299     }
300 
301     nlohmann::json data1 = nlohmann::json{
302         {"Version", Trigger::triggerVersion},
303         {"Id", TriggerParams().id()},
304         {"Name", TriggerParams().name()},
305         {"ThresholdParamsDiscriminator",
306          TriggerParams().thresholdParams().index()},
307         {"TriggerActions", utils::transform(TriggerParams().triggerActions(),
308                                             [](const auto& action) {
309                                                 return actionToString(action);
310                                             })},
311         {"ThresholdParams", utils::labeledThresholdParamsToJson(
312                                 TriggerParams().thresholdParams())},
313         {"ReportIds", TriggerParams().reportIds()},
314         {"Sensors", TriggerParams().sensors()}};
315 
316     nlohmann::json data2 = data1;
317 };
318 
319 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage)
320 {
321     triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock));
322     triggerFactoryMock.expectMake(
323         TriggerParams().id("Trigger2").name("Second Trigger"), _,
324         Ref(storageMock));
325     EXPECT_CALL(storageMock, remove(_)).Times(0);
326 
327     sut = makeTriggerManager();
328 }
329 
330 TEST_F(TestTriggerManagerStorage,
331        triggerManagerCtorRemoveDiscreteTriggerFromStorage)
332 {
333     LabeledTriggerThresholdParams thresholdParams =
334         std::vector<discrete::LabeledThresholdParam>{
335             {"userId1", discrete::Severity::warning, 15, "10.0"},
336             {"userId2", discrete::Severity::critical, 5, "20.0"}};
337 
338     data1["ThresholdParamsDiscriminator"] = thresholdParams.index();
339 
340     data1["ThresholdParams"] =
341         utils::labeledThresholdParamsToJson(thresholdParams);
342 
343     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
344 
345     sut = makeTriggerManager();
346 }
347 
348 TEST_F(TestTriggerManagerStorage,
349        triggerManagerCtorRemoveDiscreteTriggerFromStorage2)
350 {
351     data1["IsDiscrete"] = true;
352 
353     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
354 
355     sut = makeTriggerManager();
356 }
357 
358 TEST_F(TestTriggerManagerStorage,
359        triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage)
360 {
361     data1["Version"] = Trigger::triggerVersion - 1;
362 
363     triggerFactoryMock.expectMake(
364         TriggerParams().id("Trigger2").name("Second Trigger"), _,
365         Ref(storageMock));
366     EXPECT_CALL(storageMock, remove(FilePath("trigger1")));
367 
368     sut = makeTriggerManager();
369 }
370