xref: /openbmc/telemetry/tests/src/test_report_manager.cpp (revision 32305f14d8a7560980735c04fbb2067d633e08d8)
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/dbus_path_utils.hpp"
12 #include "utils/string_utils.hpp"
13 #include "utils/transform.hpp"
14 #include "utils/tstring.hpp"
15 
16 using namespace testing;
17 using namespace std::string_literals;
18 using namespace std::chrono_literals;
19 
20 class TestReportManager : public Test
21 {
22   public:
23     ReportParams reportParams;
24 
25     std::unique_ptr<ReportFactoryMock> reportFactoryMockPtr =
26         std::make_unique<StrictMock<ReportFactoryMock>>();
27     ReportFactoryMock& reportFactoryMock = *reportFactoryMockPtr;
28 
29     std::unique_ptr<StorageMock> storageMockPtr =
30         std::make_unique<NiceMock<StorageMock>>();
31     StorageMock& storageMock = *storageMockPtr;
32 
33     std::unique_ptr<ReportMock> reportMockPtr =
34         std::make_unique<NiceMock<ReportMock>>(reportParams.reportId());
35     ReportMock& reportMock = *reportMockPtr;
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>(std::move(reportFactoryMockPtr),
47                                               std::move(storageMockPtr),
48                                               DbusEnvironment::getObjServer());
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, returnsPropertySupportedOperationTypes)
110 {
111     EXPECT_THAT(
112         getProperty<std::vector<std::string>>("SupportedOperationTypes"),
113         UnorderedElementsAre("Maximum", "Minimum", "Average", "Summation"));
114 }
115 
116 TEST_F(TestReportManager, addReport)
117 {
118     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
119     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
120         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
121 
122     auto [ec, path] = addReport(reportParams);
123     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
124     EXPECT_THAT(path, Eq(reportMock.getPath()));
125 }
126 
127 TEST_F(TestReportManager, addOnChangeReport)
128 {
129     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
130     reportFactoryMock
131         .expectMake(reportParams.reportingType(ReportingType::onChange),
132                     Ref(*sut), Ref(storageMock))
133         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
134 
135     auto [ec, path] = addReport(reportParams);
136     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
137     EXPECT_THAT(path, Eq(reportMock.getPath()));
138 }
139 
140 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport)
141 {
142     reportParams.reportId("ReportName");
143     reportParams.reportName("ReportName");
144 
145     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
146 
147     auto [ec, path] = addReport(reportParams.reportId(""));
148 
149     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
150     EXPECT_THAT(path, Eq("/ReportName"));
151 }
152 
153 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace)
154 {
155     reportParams.reportId("Prefix/ReportName");
156     reportParams.reportName("ReportName");
157 
158     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
159 
160     auto [ec, path] = addReport(reportParams.reportId("Prefix/"));
161 
162     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
163     EXPECT_THAT(path, Eq("/Prefix/ReportName"));
164 }
165 
166 TEST_F(TestReportManager, addReportWithMaxLengthId)
167 {
168     std::string reportId = utils::string_utils::getMaxId();
169     reportParams.reportId(reportId);
170     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
171 
172     auto [ec, path] = addReport(reportParams);
173 
174     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
175     EXPECT_THAT(path, Eq("/"s + reportId));
176 }
177 
178 TEST_F(TestReportManager, addReportWithMaxLengthPrefix)
179 {
180     std::string reportId = utils::string_utils::getMaxPrefix() + "/MyId";
181     reportParams.reportId(reportId);
182     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
183 
184     auto [ec, path] = addReport(reportParams);
185 
186     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
187     EXPECT_THAT(path, Eq("/"s + reportId));
188 }
189 
190 TEST_F(TestReportManager, addReportWithMaxLengthName)
191 {
192     reportParams.reportName(utils::string_utils::getMaxName());
193     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
194 
195     auto [ec, path] = addReport(reportParams);
196 
197     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
198     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
199 }
200 
201 TEST_F(TestReportManager, addReportWithMaxLengthMetricId)
202 {
203     namespace ts = utils::tstring;
204     std::vector<LabeledMetricParameters> newMetricParams{
205         {LabeledMetricParameters{
206             {LabeledSensorInfo{"Service",
207                                "/xyz/openbmc_project/sensors/power/p1",
208                                "metadata1"}},
209             OperationType::avg,
210             utils::string_utils::getMaxId(),
211             CollectionTimeScope::point,
212             CollectionDuration(Milliseconds(0u))}}};
213 
214     reportParams.metricParameters(newMetricParams);
215     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
216 
217     auto [ec, path] = addReport(reportParams);
218 
219     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
220     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
221 }
222 
223 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongFullId)
224 {
225     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
226         .Times(0);
227 
228     reportParams.reportId(
229         std::string(utils::constants::maxReportFullIdLength + 1, 'z'));
230 
231     auto [ec, path] = addReport(reportParams);
232 
233     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
234     EXPECT_THAT(path, Eq(std::string()));
235 }
236 
237 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongId)
238 {
239     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
240         .Times(0);
241 
242     reportParams.reportId(utils::string_utils::getTooLongId());
243 
244     auto [ec, path] = addReport(reportParams);
245 
246     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
247     EXPECT_THAT(path, Eq(std::string()));
248 }
249 
250 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongPrefix)
251 {
252     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
253         .Times(0);
254 
255     reportParams.reportId(utils::string_utils::getTooLongPrefix() + "/MyId");
256 
257     auto [ec, path] = addReport(reportParams);
258 
259     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
260     EXPECT_THAT(path, Eq(std::string()));
261 }
262 
263 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooManyPrefixes)
264 {
265     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
266         .Times(0);
267 
268     std::string reportId;
269     for (size_t i = 0; i < utils::constants::maxPrefixesInId + 1; i++)
270     {
271         reportId += "prefix/";
272     }
273     reportId += "MyId";
274 
275     reportParams.reportId(reportId);
276 
277     auto [ec, path] = addReport(reportParams);
278 
279     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
280     EXPECT_THAT(path, Eq(std::string()));
281 }
282 
283 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName)
284 {
285     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
286         .Times(0);
287 
288     reportParams.reportName(utils::string_utils::getTooLongName());
289 
290     auto [ec, path] = addReport(reportParams);
291 
292     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
293     EXPECT_THAT(path, Eq(std::string()));
294 }
295 
296 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongMetricId)
297 {
298     namespace ts = utils::tstring;
299 
300     std::vector<LabeledMetricParameters> newMetricParams{
301         {LabeledMetricParameters{
302             {LabeledSensorInfo{"Service",
303                                "/xyz/openbmc_project/sensors/power/p1",
304                                "metadata1"}},
305             OperationType::avg,
306             utils::string_utils::getTooLongId(),
307             CollectionTimeScope::point,
308             CollectionDuration(Milliseconds(0u))}}};
309 
310     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
311         .Times(0);
312 
313     reportParams.metricParameters(newMetricParams);
314 
315     auto [ec, path] = addReport(reportParams);
316 
317     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
318     EXPECT_THAT(path, Eq(std::string()));
319 }
320 
321 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
322 {
323     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
324         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
325 
326     addReport(reportParams);
327 
328     auto [ec, path] = addReport(reportParams);
329 
330     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
331     EXPECT_THAT(path, Eq(std::string()));
332 }
333 
334 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
335 {
336     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
337         .Times(0);
338 
339     reportParams.reportingType(ReportingType::periodic);
340     reportParams.interval(ReportManager::minInterval - 1ms);
341 
342     auto [ec, path] = addReport(reportParams);
343 
344     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
345     EXPECT_THAT(path, Eq(std::string()));
346 }
347 
348 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
349 {
350     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
351         .Times(0);
352 
353     auto [ec, path] = addReport(
354         reportParams.reportName(), "InvalidReportingType",
355         utils::transform(reportParams.reportActions(),
356                          [](const auto v) { return utils::enumToString(v); }),
357         reportParams.interval().count(),
358         toReadingParameters(reportParams.metricParameters()));
359 
360     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
361     EXPECT_THAT(path, Eq(std::string()));
362 }
363 
364 TEST_F(TestReportManager,
365        DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected)
366 {
367     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
368         .Times(0);
369 
370     reportParams.metricParameters(
371         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
372             {LabeledSensorInfo{"Service",
373                                "/xyz/openbmc_project/sensors/power/p1",
374                                "Metadata1"}},
375             OperationType::avg,
376             "MetricId1",
377             CollectionTimeScope::point,
378             CollectionDuration(Milliseconds(0u))}}});
379 
380     auto metricParams = reportParams.metricParameters();
381     auto& metricParamsVec =
382         metricParams[0].at_label<utils::tstring::SensorPath>();
383 
384     for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++)
385     {
386         metricParamsVec.emplace_back(LabeledSensorInfo{
387             "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"});
388     }
389 
390     reportParams.metricParameters(std::move(metricParams));
391 
392     auto [ec, path] = addReport(reportParams);
393 
394     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
395     EXPECT_THAT(path, Eq(std::string()));
396 }
397 
398 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected)
399 {
400     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
401         .Times(0);
402 
403     auto metricParams = std::vector<LabeledMetricParameters>{};
404 
405     for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++)
406     {
407         metricParams.emplace_back(
408             LabeledMetricParameters{{},
409                                     OperationType::avg,
410                                     "MetricId1",
411                                     CollectionTimeScope::point,
412                                     CollectionDuration(Milliseconds(0u))});
413     }
414 
415     reportParams.metricParameters(std::move(metricParams));
416 
417     auto [ec, path] = addReport(reportParams);
418 
419     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
420     EXPECT_THAT(path, Eq(std::string()));
421 }
422 
423 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax)
424 {
425     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
426         .Times(0);
427 
428     reportParams.appendLimit(ReportManager::maxAppendLimit + 1);
429 
430     auto [ec, path] = addReport(reportParams);
431 
432     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
433     EXPECT_THAT(path, Eq(std::string()));
434 }
435 
436 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed)
437 {
438     reportParams.appendLimit(std::numeric_limits<uint64_t>::max());
439 
440     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
441     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
442         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
443 
444     auto [ec, path] = addReport(reportParams);
445     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
446     EXPECT_THAT(path, Eq(reportMock.getPath()));
447 }
448 
449 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
450 {
451     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
452         .Times(ReportManager::maxReports);
453 
454     for (size_t i = 0; i < ReportManager::maxReports; i++)
455     {
456         reportParams.reportId(reportParams.reportName() + std::to_string(i));
457 
458         auto [ec, path] = addReport(reportParams);
459         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
460     }
461 
462     reportParams.reportId(reportParams.reportName() +
463                           std::to_string(ReportManager::maxReports));
464     auto [ec, path] = addReport(reportParams);
465 
466     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
467     EXPECT_THAT(path, Eq(std::string()));
468 }
469 
470 TEST_F(TestReportManager, removeReport)
471 {
472     {
473         InSequence seq;
474         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
475         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
476             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
477         EXPECT_CALL(reportMock, Die());
478         EXPECT_CALL(checkPoint, Call("end"));
479     }
480 
481     addReport(reportParams);
482     sut->removeReport(&reportMock);
483     checkPoint.Call("end");
484 }
485 
486 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
487 {
488     {
489         InSequence seq;
490         EXPECT_CALL(checkPoint, Call("end"));
491         EXPECT_CALL(reportMock, Die());
492     }
493 
494     sut->removeReport(&reportMock);
495     checkPoint.Call("end");
496 }
497 
498 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
499 {
500     {
501         InSequence seq;
502         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
503         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
504             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
505         EXPECT_CALL(reportMock, Die());
506         EXPECT_CALL(checkPoint, Call("end"));
507     }
508 
509     addReport(reportParams);
510     sut->removeReport(&reportMock);
511     sut->removeReport(&reportMock);
512     checkPoint.Call("end");
513 }
514 
515 class TestReportManagerWithAggregationOperationType :
516     public TestReportManager,
517     public WithParamInterface<OperationType>
518 {
519   public:
520     OperationType operationType = GetParam();
521 };
522 
523 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
524                          Values(OperationType::max, OperationType::min,
525                                 OperationType::avg, OperationType::sum));
526 
527 TEST_P(TestReportManagerWithAggregationOperationType,
528        addReportWithDifferentOperationTypes)
529 {
530     reportParams.metricParameters(
531         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
532             {LabeledSensorInfo{"Service",
533                                "/xyz/openbmc_project/sensors/power/p1",
534                                "Metadata1"}},
535             operationType,
536             "MetricId1",
537             CollectionTimeScope::point,
538             CollectionDuration(Milliseconds(0u))}}});
539 
540     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
541         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
542 
543     auto [ec, path] = addReport(reportParams);
544 
545     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
546     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
547 }
548 
549 class TestReportManagerStorage : public TestReportManager
550 {
551   public:
552     using FilePath = interfaces::JsonStorage::FilePath;
553     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
554 
555     void SetUp() override
556     {
557         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0);
558 
559         ON_CALL(storageMock, list())
560             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
561         ON_CALL(storageMock, load(FilePath("report1")))
562             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
563     }
564 
565     void makeReportManager()
566     {
567         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
568                                               std::move(storageMockPtr),
569                                               DbusEnvironment::getObjServer());
570     }
571 
572     nlohmann::json data = nlohmann::json{
573         {"Enabled", reportParams.enabled()},
574         {"Version", Report::reportVersion},
575         {"Id", reportParams.reportId()},
576         {"Name", reportParams.reportName()},
577         {"ReportingType", utils::toUnderlying(reportParams.reportingType())},
578         {"ReportActions", reportParams.reportActions()},
579         {"Interval", reportParams.interval().count()},
580         {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())},
581         {"AppendLimit", reportParams.appendLimit()},
582         {"ReadingParameters", reportParams.metricParameters()}};
583 };
584 
585 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage)
586 {
587     reportFactoryMock.expectMake(reportParams, _, Ref(storageMock));
588 
589     makeReportManager();
590 }
591 
592 TEST_F(TestReportManagerStorage,
593        reportManagerCtorRemoveFileIfVersionDoesNotMatch)
594 {
595     data["Version"] = Report::reportVersion - 1;
596 
597     EXPECT_CALL(storageMock, remove(FilePath("report1")));
598 
599     makeReportManager();
600 }
601 
602 TEST_F(TestReportManagerStorage,
603        reportManagerCtorRemoveFileIfIntervalHasWrongType)
604 {
605     data["Interval"] = "1000";
606 
607     EXPECT_CALL(storageMock, remove(FilePath("report1")));
608 
609     makeReportManager();
610 }
611