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",
32             params.name(), params.isDiscrete(), params.logToJournal(),
33             params.logToRedfish(), params.updateReport(), sensorInfos,
34             params.reportNames(),
35             std::visit(utils::FromLabeledThresholdParamConversion(),
36                        params.thresholdParams()));
37         return DbusEnvironment::waitForFuture(addTriggerPromise.get_future());
38     }
39 
40     std::unique_ptr<TriggerManager> makeTriggerManager()
41     {
42         return std::make_unique<TriggerManager>(
43             std::move(triggerFactoryMockPtr), std::move(storageMockPtr),
44             DbusEnvironment::getObjServer());
45     }
46 
47     void SetUp() override
48     {
49         sut = makeTriggerManager();
50     }
51 
52     std::unique_ptr<StorageMock> storageMockPtr =
53         std::make_unique<NiceMock<StorageMock>>();
54     StorageMock& storageMock = *storageMockPtr;
55     std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr =
56         std::make_unique<NiceMock<TriggerFactoryMock>>();
57     TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr;
58     std::unique_ptr<TriggerMock> triggerMockPtr =
59         std::make_unique<NiceMock<TriggerMock>>(TriggerParams().name());
60     TriggerMock& triggerMock = *triggerMockPtr;
61     std::unique_ptr<TriggerManager> sut;
62     MockFunction<void(std::string)> checkPoint;
63 };
64 
65 TEST_F(TestTriggerManager, addTrigger)
66 {
67     triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
68         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
69 
70     auto [ec, path] = addTrigger(TriggerParams());
71     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
72     EXPECT_THAT(path, Eq(triggerMock.getPath()));
73 }
74 
75 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds)
76 {
77     TriggerParams triggerParamsDiscrete;
78     auto thresholds = std::vector<discrete::LabeledThresholdParam>{
79         {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"},
80         {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"},
81         {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}};
82 
83     triggerParamsDiscrete.thresholdParams(thresholds).isDiscrete(true);
84 
85     auto [ec, path] = addTrigger(triggerParamsDiscrete);
86     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
87     EXPECT_THAT(path, Eq(triggerMock.getPath()));
88 }
89 
90 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds)
91 {
92     TriggerParams triggerParamsDiscrete;
93     auto thresholds = std::vector<discrete::LabeledThresholdParam>();
94 
95     triggerParamsDiscrete.thresholdParams(thresholds).isDiscrete(true);
96 
97     auto [ec, path] = addTrigger(triggerParamsDiscrete);
98     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
99     EXPECT_THAT(path, Eq(triggerMock.getPath()));
100 }
101 
102 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice)
103 {
104     triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
105         .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
106 
107     addTrigger(TriggerParams());
108 
109     auto [ec, path] = addTrigger(TriggerParams());
110     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
111     EXPECT_THAT(path, Eq(std::string()));
112 }
113 
114 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached)
115 {
116     auto triggerParams = TriggerParams();
117 
118     triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
119         .Times(TriggerManager::maxTriggers);
120 
121     for (size_t i = 0; i < TriggerManager::maxTriggers; i++)
122     {
123         triggerParams.name(TriggerParams().name() + std::to_string(i));
124 
125         auto [ec, path] = addTrigger(triggerParams);
126         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
127     }
128 
129     triggerParams.name(TriggerParams().name() +
130                        std::to_string(TriggerManager::maxTriggers));
131     auto [ec, path] = addTrigger(triggerParams);
132     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
133     EXPECT_THAT(path, Eq(std::string()));
134 }
135 
136 TEST_F(TestTriggerManager, removeTrigger)
137 {
138     {
139         InSequence seq;
140         triggerFactoryMock
141             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
142             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
143         EXPECT_CALL(triggerMock, Die());
144         EXPECT_CALL(checkPoint, Call("end"));
145     }
146 
147     addTrigger(TriggerParams());
148     sut->removeTrigger(&triggerMock);
149     checkPoint.Call("end");
150 }
151 
152 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect)
153 {
154     {
155         InSequence seq;
156         EXPECT_CALL(checkPoint, Call("end"));
157         EXPECT_CALL(triggerMock, Die());
158     }
159 
160     sut->removeTrigger(&triggerMock);
161     checkPoint.Call("end");
162 }
163 
164 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect)
165 {
166     {
167         InSequence seq;
168         triggerFactoryMock
169             .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
170             .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
171         EXPECT_CALL(triggerMock, Die());
172         EXPECT_CALL(checkPoint, Call("end"));
173     }
174 
175     addTrigger(TriggerParams());
176     sut->removeTrigger(&triggerMock);
177     sut->removeTrigger(&triggerMock);
178     checkPoint.Call("end");
179 }
180 
181 class TestTriggerManagerStorage : public TestTriggerManager
182 {
183   public:
184     using FilePath = interfaces::JsonStorage::FilePath;
185     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
186 
187     void SetUp() override
188     {
189         ON_CALL(storageMock, list())
190             .WillByDefault(Return(std::vector<FilePath>{
191                 {FilePath("trigger1")}, {FilePath("trigger2")}}));
192 
193         ON_CALL(storageMock, load(FilePath("trigger1")))
194             .WillByDefault(InvokeWithoutArgs([this] { return data1; }));
195 
196         data2["Name"] = "Trigger2";
197         ON_CALL(storageMock, load(FilePath("trigger2")))
198             .WillByDefault(InvokeWithoutArgs([this] { return data2; }));
199     }
200 
201     nlohmann::json data1 = nlohmann::json{
202         {"Version", Trigger::triggerVersion},
203         {"Name", TriggerParams().name()},
204         {"ThresholdParamsDiscriminator",
205          TriggerParams().thresholdParams().index()},
206         {"IsDiscrete", TriggerParams().isDiscrete()},
207         {"LogToJournal", TriggerParams().logToJournal()},
208         {"LogToRedfish", TriggerParams().logToRedfish()},
209         {"UpdateReport", TriggerParams().updateReport()},
210         {"ThresholdParams", utils::labeledThresholdParamsToJson(
211                                 TriggerParams().thresholdParams())},
212         {"ReportNames", TriggerParams().reportNames()},
213         {"Sensors", TriggerParams().sensors()}};
214 
215     nlohmann::json data2 = data1;
216 };
217 
218 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage)
219 {
220     triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock));
221     triggerFactoryMock.expectMake(TriggerParams().name("Trigger2"), _,
222                                   Ref(storageMock));
223     EXPECT_CALL(storageMock, remove(_)).Times(0);
224 
225     sut = makeTriggerManager();
226 }
227 
228 TEST_F(TestTriggerManagerStorage,
229        triggerManagerCtorRemoveDiscreteTriggerFromStorage)
230 {
231     LabeledTriggerThresholdParams thresholdParams =
232         std::vector<discrete::LabeledThresholdParam>{
233             {"userId1", discrete::Severity::warning, 15, "10.0"},
234             {"userId2", discrete::Severity::critical, 5, "20.0"}};
235 
236     data1["ThresholdParamsDiscriminator"] = thresholdParams.index();
237 
238     data1["ThresholdParams"] =
239         utils::labeledThresholdParamsToJson(thresholdParams);
240 
241     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
242 
243     sut = makeTriggerManager();
244 }
245 
246 TEST_F(TestTriggerManagerStorage,
247        triggerManagerCtorRemoveDiscreteTriggerFromStorage2)
248 {
249     data1["IsDiscrete"] = true;
250 
251     EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
252 
253     sut = makeTriggerManager();
254 }
255 
256 TEST_F(TestTriggerManagerStorage,
257        triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage)
258 {
259     data1["Version"] = Trigger::triggerVersion - 1;
260 
261     triggerFactoryMock.expectMake(TriggerParams().name("Trigger2"), _,
262                                   Ref(storageMock));
263     EXPECT_CALL(storageMock, remove(FilePath("trigger1")));
264 
265     sut = makeTriggerManager();
266 }
267