xref: /openbmc/telemetry/tests/src/test_report_manager.cpp (revision b4ef22e4d365bde35a7fce7950033c9271c68ce7)
1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "interfaces/trigger_manager.hpp"
4 #include "mocks/json_storage_mock.hpp"
5 #include "mocks/report_factory_mock.hpp"
6 #include "mocks/trigger_manager_mock.hpp"
7 #include "params/report_params.hpp"
8 #include "report.hpp"
9 #include "report_manager.hpp"
10 #include "utils/conversion.hpp"
11 #include "utils/transform.hpp"
12 
13 using namespace testing;
14 using namespace std::string_literals;
15 using namespace std::chrono_literals;
16 
17 class TestReportManager : public Test
18 {
19   public:
20     ReportParams reportParams;
21 
22     std::unique_ptr<ReportFactoryMock> reportFactoryMockPtr =
23         std::make_unique<StrictMock<ReportFactoryMock>>();
24     ReportFactoryMock& reportFactoryMock = *reportFactoryMockPtr;
25 
26     std::unique_ptr<StorageMock> storageMockPtr =
27         std::make_unique<NiceMock<StorageMock>>();
28     StorageMock& storageMock = *storageMockPtr;
29 
30     std::unique_ptr<ReportMock> reportMockPtr =
31         std::make_unique<NiceMock<ReportMock>>(reportParams.reportId());
32     ReportMock& reportMock = *reportMockPtr;
33 
34     std::unique_ptr<interfaces::TriggerManager> triggerManagerMockPtr =
35         std::make_unique<NiceMock<TriggerManagerMock>>();
36 
37     std::unique_ptr<ReportManager> sut;
38 
39     MockFunction<void(std::string)> checkPoint;
40 
41     void SetUp() override
42     {
43         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _))
44             .Times(AnyNumber());
45 
46         sut = std::make_unique<ReportManager>(
47             std::move(reportFactoryMockPtr), std::move(storageMockPtr),
48             DbusEnvironment::getObjServer(), triggerManagerMockPtr);
49     }
50 
51     void TearDown() override
52     {
53         DbusEnvironment::synchronizeIoc();
54     }
55 
56     template <class... Args>
57     requires(sizeof...(Args) > 1)
58         std::pair<boost::system::error_code, std::string> addReport(
59             Args&&... args)
60     {
61         std::promise<std::pair<boost::system::error_code, std::string>>
62             addReportPromise;
63         DbusEnvironment::getBus()->async_method_call(
64             [&addReportPromise](boost::system::error_code ec,
65                                 const std::string& path) {
66                 addReportPromise.set_value({ec, path});
67             },
68             DbusEnvironment::serviceName(), ReportManager::reportManagerPath,
69             ReportManager::reportManagerIfaceName, "AddReportFutureVersion",
70             std::forward<Args>(args)...);
71         return DbusEnvironment::waitForFuture(addReportPromise.get_future());
72     }
73 
74     auto addReport(const ReportParams& params)
75     {
76         return addReport(params.reportId(), params.reportName(),
77                          utils::enumToString(params.reportingType()),
78                          utils::enumToString(params.reportUpdates()),
79                          params.appendLimit(),
80                          utils::transform(params.reportActions(),
81                                           [](const auto v) {
82                                               return utils::enumToString(v);
83                                           }),
84                          params.interval().count(),
85                          toReadingParameters(params.metricParameters()));
86     }
87 
88     template <class T>
89     static T getProperty(const std::string& property)
90     {
91         return DbusEnvironment::getProperty<T>(
92             ReportManager::reportManagerPath,
93             ReportManager::reportManagerIfaceName, property);
94     }
95 };
96 
97 TEST_F(TestReportManager, minInterval)
98 {
99     EXPECT_THAT(getProperty<uint64_t>("MinInterval"),
100                 Eq(ReportManager::minInterval.count()));
101 }
102 
103 TEST_F(TestReportManager, maxReports)
104 {
105     EXPECT_THAT(getProperty<size_t>("MaxReports"),
106                 Eq(ReportManager::maxReports));
107 }
108 
109 TEST_F(TestReportManager, addReport)
110 {
111     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
112     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
113         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
114 
115     auto [ec, path] = addReport(reportParams);
116     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
117     EXPECT_THAT(path, Eq(reportMock.getPath()));
118 }
119 
120 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport)
121 {
122     reportParams.reportId("ReportName");
123     reportParams.reportName("ReportName");
124 
125     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
126 
127     auto [ec, path] = addReport(reportParams.reportId(""));
128 
129     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
130     EXPECT_THAT(path, Eq("/ReportName"));
131 }
132 
133 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace)
134 {
135     reportParams.reportId("Prefix/ReportName");
136     reportParams.reportName("ReportName");
137 
138     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
139 
140     auto [ec, path] = addReport(reportParams.reportId("Prefix/"));
141 
142     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
143     EXPECT_THAT(path, Eq("/Prefix/ReportName"));
144 }
145 
146 TEST_F(TestReportManager, addReportWithMaxLengthId)
147 {
148     std::string reportId(ReportManager::maxReportIdLength, 'z');
149     reportParams.reportId(reportId);
150     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
151 
152     auto [ec, path] = addReport(reportParams);
153 
154     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
155     EXPECT_THAT(path, Eq("/"s + reportId));
156 }
157 
158 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName)
159 {
160     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
161         .Times(0);
162 
163     reportParams.reportId(
164         std::string(ReportManager::maxReportIdLength + 1, 'z'));
165 
166     auto [ec, path] = addReport(reportParams);
167 
168     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
169     EXPECT_THAT(path, Eq(std::string()));
170 }
171 
172 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
173 {
174     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
175         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
176 
177     addReport(reportParams);
178 
179     auto [ec, path] = addReport(reportParams);
180 
181     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
182     EXPECT_THAT(path, Eq(std::string()));
183 }
184 
185 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
186 {
187     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
188         .Times(0);
189 
190     reportParams.reportingType(ReportingType::periodic);
191     reportParams.interval(reportParams.interval() - 1ms);
192 
193     auto [ec, path] = addReport(reportParams);
194 
195     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
196     EXPECT_THAT(path, Eq(std::string()));
197 }
198 
199 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
200 {
201     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
202         .Times(0);
203 
204     auto [ec, path] = addReport(
205         reportParams.reportName(), "InvalidReportingType",
206         utils::transform(reportParams.reportActions(),
207                          [](const auto v) { return utils::enumToString(v); }),
208         reportParams.interval().count(),
209         toReadingParameters(reportParams.metricParameters()));
210 
211     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
212     EXPECT_THAT(path, Eq(std::string()));
213 }
214 
215 TEST_F(TestReportManager,
216        DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected)
217 {
218     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
219         .Times(0);
220 
221     reportParams.metricParameters(
222         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
223             {LabeledSensorInfo{"Service",
224                                "/xyz/openbmc_project/sensors/power/p1",
225                                "Metadata1"}},
226             OperationType::single,
227             "MetricId1",
228             CollectionTimeScope::point,
229             CollectionDuration(Milliseconds(0u))}}});
230 
231     auto metricParams = reportParams.metricParameters();
232     auto& metricParamsVec =
233         metricParams[0].at_label<utils::tstring::SensorPath>();
234 
235     for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++)
236     {
237         metricParamsVec.emplace_back(LabeledSensorInfo{
238             "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"});
239     }
240 
241     reportParams.metricParameters(std::move(metricParams));
242 
243     auto [ec, path] = addReport(reportParams);
244 
245     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
246     EXPECT_THAT(path, Eq(std::string()));
247 }
248 
249 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected)
250 {
251     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
252         .Times(0);
253 
254     auto metricParams = std::vector<LabeledMetricParameters>{};
255 
256     for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++)
257     {
258         metricParams.emplace_back(
259             LabeledMetricParameters{{},
260                                     OperationType::single,
261                                     "MetricId1",
262                                     CollectionTimeScope::point,
263                                     CollectionDuration(Milliseconds(0u))});
264     }
265 
266     reportParams.metricParameters(std::move(metricParams));
267 
268     auto [ec, path] = addReport(reportParams);
269 
270     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
271     EXPECT_THAT(path, Eq(std::string()));
272 }
273 
274 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax)
275 {
276     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
277         .Times(0);
278 
279     reportParams.appendLimit(ReportManager::maxAppendLimit + 1);
280 
281     auto [ec, path] = addReport(reportParams);
282 
283     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
284     EXPECT_THAT(path, Eq(std::string()));
285 }
286 
287 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed)
288 {
289     reportParams.appendLimit(std::numeric_limits<uint64_t>::max());
290 
291     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
292     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
293         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
294 
295     auto [ec, path] = addReport(reportParams);
296     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
297     EXPECT_THAT(path, Eq(reportMock.getPath()));
298 }
299 
300 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
301 {
302     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
303         .Times(ReportManager::maxReports);
304 
305     for (size_t i = 0; i < ReportManager::maxReports; i++)
306     {
307         reportParams.reportId(reportParams.reportName() + std::to_string(i));
308 
309         auto [ec, path] = addReport(reportParams);
310         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
311     }
312 
313     reportParams.reportId(reportParams.reportName() +
314                           std::to_string(ReportManager::maxReports));
315     auto [ec, path] = addReport(reportParams);
316 
317     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
318     EXPECT_THAT(path, Eq(std::string()));
319 }
320 
321 TEST_F(TestReportManager, removeReport)
322 {
323     {
324         InSequence seq;
325         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
326         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
327             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
328         EXPECT_CALL(reportMock, Die());
329         EXPECT_CALL(checkPoint, Call("end"));
330     }
331 
332     addReport(reportParams);
333     sut->removeReport(&reportMock);
334     checkPoint.Call("end");
335 }
336 
337 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
338 {
339     {
340         InSequence seq;
341         EXPECT_CALL(checkPoint, Call("end"));
342         EXPECT_CALL(reportMock, Die());
343     }
344 
345     sut->removeReport(&reportMock);
346     checkPoint.Call("end");
347 }
348 
349 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
350 {
351     {
352         InSequence seq;
353         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
354         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
355             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
356         EXPECT_CALL(reportMock, Die());
357         EXPECT_CALL(checkPoint, Call("end"));
358     }
359 
360     addReport(reportParams);
361     sut->removeReport(&reportMock);
362     sut->removeReport(&reportMock);
363     checkPoint.Call("end");
364 }
365 
366 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport)
367 {
368     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
369         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
370     EXPECT_CALL(reportMock, updateReadings());
371 
372     addReport(reportParams);
373     sut->updateReport(reportParams.reportId());
374 }
375 
376 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist)
377 {
378     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
379         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
380     EXPECT_CALL(reportMock, updateReadings()).Times(0);
381 
382     addReport(reportParams);
383     sut->updateReport("NotAReport");
384 }
385 
386 TEST_F(TestReportManager, updateTriggerIdsUpdatesThemForExistReport)
387 {
388     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
389         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
390     EXPECT_CALL(reportMock, updateTriggerIds("Trigger1", TriggerIdUpdate::Add));
391     EXPECT_CALL(reportMock,
392                 updateTriggerIds("Trigger2", TriggerIdUpdate::Remove));
393 
394     addReport(reportParams);
395     sut->updateTriggerIds(reportParams.reportId(), "Trigger1",
396                           TriggerIdUpdate::Add);
397 
398     sut->updateTriggerIds(reportParams.reportId(), "Trigger2",
399                           TriggerIdUpdate::Remove);
400 }
401 
402 TEST_F(TestReportManager, updateTriggerIdsDoNothingIfReportDoesNotExist)
403 {
404     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
405         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
406     EXPECT_CALL(reportMock, updateTriggerIds(_, _)).Times(0);
407 
408     addReport(reportParams);
409     sut->updateTriggerIds("NotAReport", "Trigger1", TriggerIdUpdate::Add);
410 }
411 
412 class TestReportManagerWithAggregationOperationType :
413     public TestReportManager,
414     public WithParamInterface<OperationType>
415 {
416   public:
417     OperationType operationType = GetParam();
418 };
419 
420 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
421                          Values(OperationType::single, OperationType::max,
422                                 OperationType::min, OperationType::avg,
423                                 OperationType::sum));
424 
425 TEST_P(TestReportManagerWithAggregationOperationType,
426        addReportWithDifferentOperationTypes)
427 {
428     reportParams.metricParameters(
429         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
430             {LabeledSensorInfo{"Service",
431                                "/xyz/openbmc_project/sensors/power/p1",
432                                "Metadata1"}},
433             operationType,
434             "MetricId1",
435             CollectionTimeScope::point,
436             CollectionDuration(Milliseconds(0u))}}});
437 
438     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
439         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
440 
441     auto [ec, path] = addReport(reportParams);
442 
443     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
444     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
445 }
446 
447 class TestReportManagerStorage : public TestReportManager
448 {
449   public:
450     using FilePath = interfaces::JsonStorage::FilePath;
451     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
452 
453     void SetUp() override
454     {
455         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0);
456 
457         ON_CALL(storageMock, list())
458             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
459         ON_CALL(storageMock, load(FilePath("report1")))
460             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
461     }
462 
463     void makeReportManager()
464     {
465         sut = std::make_unique<ReportManager>(
466             std::move(reportFactoryMockPtr), std::move(storageMockPtr),
467             DbusEnvironment::getObjServer(), triggerManagerMockPtr);
468     }
469 
470     nlohmann::json data = nlohmann::json{
471         {"Enabled", reportParams.enabled()},
472         {"Version", Report::reportVersion},
473         {"Id", reportParams.reportId()},
474         {"Name", reportParams.reportName()},
475         {"ReportingType", utils::toUnderlying(reportParams.reportingType())},
476         {"ReportActions", reportParams.reportActions()},
477         {"Interval", reportParams.interval().count()},
478         {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())},
479         {"AppendLimit", reportParams.appendLimit()},
480         {"ReadingParameters", reportParams.metricParameters()}};
481 };
482 
483 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage)
484 {
485     reportFactoryMock.expectMake(reportParams, _, Ref(storageMock));
486 
487     makeReportManager();
488 }
489 
490 TEST_F(TestReportManagerStorage,
491        reportManagerCtorRemoveFileIfVersionDoesNotMatch)
492 {
493     data["Version"] = Report::reportVersion - 1;
494 
495     EXPECT_CALL(storageMock, remove(FilePath("report1")));
496 
497     makeReportManager();
498 }
499 
500 TEST_F(TestReportManagerStorage,
501        reportManagerCtorRemoveFileIfIntervalHasWrongType)
502 {
503     data["Interval"] = "1000";
504 
505     EXPECT_CALL(storageMock, remove(FilePath("report1")));
506 
507     makeReportManager();
508 }
509