1 #include "rde/external_storer_file.hpp"
2 
3 #include <boost/asio/io_context.hpp>
4 
5 #include <string_view>
6 
7 #include <gmock/gmock-matchers.h>
8 #include <gmock/gmock.h>
9 #include <gtest/gtest.h>
10 
11 namespace bios_bmc_smm_error_logger
12 {
13 namespace rde
14 {
15 
16 using ::testing::_;
17 using ::testing::DoAll;
18 using ::testing::InSequence;
19 using ::testing::Return;
20 using ::testing::SaveArg;
21 
22 class MockFileWriter : public FileHandlerInterface
23 {
24   public:
25     MOCK_METHOD(bool, createFolder, (const std::string& path),
26                 (const, override));
27     MOCK_METHOD(bool, createFile,
28                 (const std::string& path, const nlohmann::json& jsonPdr),
29                 (const, override));
30     MOCK_METHOD(bool, removeAll, (const std::string& path), (const, override));
31 };
32 
33 class ExternalStorerFileTest : public ::testing::Test
34 {
35   public:
ExternalStorerFileTest()36     ExternalStorerFileTest() :
37         conn(std::make_shared<sdbusplus::asio::connection>(io)),
38         mockFileWriter(std::make_unique<MockFileWriter>())
39     {
40         mockFileWriterPtr = dynamic_cast<MockFileWriter*>(mockFileWriter.get());
41         // Set the queue of LogEntry to 1 saved entry and 2 non saved entry
42         exStorer = std::make_unique<ExternalStorerFileInterface>(
43             conn, rootPath, std::move(mockFileWriter), 1, 2);
44     }
45 
46   protected:
47     boost::asio::io_context io;
48     std::shared_ptr<sdbusplus::asio::connection> conn;
49 
50     std::unique_ptr<FileHandlerInterface> mockFileWriter;
51     std::unique_ptr<ExternalStorerFileInterface> exStorer;
52     MockFileWriter* mockFileWriterPtr;
53     const std::string rootPath = "/some/path";
54 };
55 
TEST_F(ExternalStorerFileTest,InvalidJsonTest)56 TEST_F(ExternalStorerFileTest, InvalidJsonTest)
57 {
58     // Try an invalid JSON.
59     std::string jsonStr = "Invalid JSON";
60     EXPECT_THAT(exStorer->publishJson(jsonStr), false);
61 }
62 
TEST_F(ExternalStorerFileTest,NoOdataTypeFailureTest)63 TEST_F(ExternalStorerFileTest, NoOdataTypeFailureTest)
64 {
65     // Try a JSON without @odata.type.
66     std::string jsonStr = R"(
67       {
68         "@odata.id": "/redfish/v1/Systems/system/Memory/dimm0/MemoryMetrics",
69         "Id":"Metrics"
70       }
71     )";
72     EXPECT_THAT(exStorer->publishJson(jsonStr), false);
73 }
74 
TEST_F(ExternalStorerFileTest,LogServiceNoOdataIdTest)75 TEST_F(ExternalStorerFileTest, LogServiceNoOdataIdTest)
76 {
77     // Try a LogService without @odata.id.
78     std::string jsonStr = R"(
79       {
80         "@odata.type": "#LogService.v1_1_0.LogService","Id":"6F7-C1A7C"
81       }
82     )";
83     EXPECT_THAT(exStorer->publishJson(jsonStr), false);
84 }
85 
TEST_F(ExternalStorerFileTest,LogServiceNoIdTest)86 TEST_F(ExternalStorerFileTest, LogServiceNoIdTest)
87 {
88     // Try a LogService without Id.
89     std::string jsonStr = R"(
90       {
91         "@odata.id": "/redfish/v1/Systems/system/LogServices/6F7-C1A7C",
92         "@odata.type": "#LogService.v1_1_0.LogService"
93       }
94     )";
95     EXPECT_THAT(exStorer->publishJson(jsonStr), false);
96 }
97 
TEST_F(ExternalStorerFileTest,LogServiceTest)98 TEST_F(ExternalStorerFileTest, LogServiceTest)
99 {
100     // A valid LogService test.
101     std::string jsonStr = R"(
102       {
103         "@odata.id": "/redfish/v1/Systems/system/LogServices/6F7-C1A7C",
104         "@odata.type": "#LogService.v1_1_0.LogService","Id":"6F7-C1A7C"
105         }
106       )";
107     std::string exServiceFolder =
108         "/some/path/redfish/v1/Systems/system/LogServices/6F7-C1A7C";
109     std::string exEntriesFolder =
110         "/some/path/redfish/v1/Systems/system/LogServices/6F7-C1A7C/Entries";
111     nlohmann::json exEntriesJson = "{}"_json;
112     nlohmann::json exServiceJson = nlohmann::json::parse(jsonStr);
113     EXPECT_CALL(*mockFileWriterPtr, createFile(exServiceFolder, exServiceJson))
114         .WillOnce(Return(true));
115     EXPECT_CALL(*mockFileWriterPtr, createFile(exEntriesFolder, exEntriesJson))
116         .WillOnce(Return(true));
117     EXPECT_THAT(exStorer->publishJson(jsonStr), true);
118 }
119 
TEST_F(ExternalStorerFileTest,LogEntryWithoutLogServiceTest)120 TEST_F(ExternalStorerFileTest, LogEntryWithoutLogServiceTest)
121 {
122     // Try a LogEntry without sending a LogService first.
123     std::string jsonLogEntry = R"(
124       {
125         "@odata.type": "#LogEntry.v1_13_0.LogEntry"
126       }
127     )";
128     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), false);
129 }
130 
TEST_F(ExternalStorerFileTest,LogEntryTest)131 TEST_F(ExternalStorerFileTest, LogEntryTest)
132 {
133     InSequence s;
134     // Before sending a LogEntry, first we need to push a LogService.
135     std::string jsonLogSerivce = R"(
136       {
137         "@odata.id": "/redfish/v1/Systems/system/LogServices/6F7-C1A7C",
138         "@odata.type": "#LogService.v1_1_0.LogService","Id":"6F7-C1A7C"
139       }
140     )";
141     std::string exServiceFolder =
142         "/some/path/redfish/v1/Systems/system/LogServices/6F7-C1A7C";
143     std::string exEntriesFolder =
144         "/some/path/redfish/v1/Systems/system/LogServices/6F7-C1A7C/Entries";
145     nlohmann::json exEntriesJson = "{}"_json;
146     nlohmann::json exServiceJson = nlohmann::json::parse(jsonLogSerivce);
147     EXPECT_CALL(*mockFileWriterPtr, createFile(exServiceFolder, exServiceJson))
148         .WillOnce(Return(true));
149     EXPECT_CALL(*mockFileWriterPtr, createFile(exEntriesFolder, exEntriesJson))
150         .WillOnce(Return(true));
151     EXPECT_THAT(exStorer->publishJson(jsonLogSerivce), true);
152 
153     // Now send a LogEntry#1, which will not be deleted
154     std::string jsonLogEntry = R"(
155       {
156         "@odata.id": "/some/odata/id",
157         "@odata.type": "#LogEntry.v1_13_0.LogEntry"
158       }
159     )";
160     nlohmann::json logEntryOut;
161     std::string logPath1;
162     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
163         .WillOnce(DoAll(SaveArg<0>(&logPath1), SaveArg<1>(&logEntryOut),
164                         Return(true)));
165     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
166     EXPECT_FALSE(logPath1.empty());
167     EXPECT_NE(logEntryOut["Id"], nullptr);
168     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
169 
170     // Now send a LogEntry#2, which will be the first to be deleted
171     std::string logPath2;
172     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
173         .WillOnce(DoAll(SaveArg<0>(&logPath2), SaveArg<1>(&logEntryOut),
174                         Return(true)));
175     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
176     EXPECT_FALSE(logPath2.empty());
177     EXPECT_NE(logEntryOut["Id"], nullptr);
178     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
179 
180     // Now send a LogEntry#3
181     std::string logPath3;
182     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
183         .WillOnce(DoAll(SaveArg<0>(&logPath3), SaveArg<1>(&logEntryOut),
184                         Return(true)));
185     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
186     EXPECT_FALSE(logPath3.empty());
187     EXPECT_NE(logEntryOut["Id"], nullptr);
188     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
189 
190     // Now send a LogEntry#4, we expect the LogEntry#2 to be deleted
191     std::string logPath4;
192     EXPECT_CALL(*mockFileWriterPtr, removeAll(logPath2)).WillOnce(Return(true));
193     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
194         .WillOnce(DoAll(SaveArg<0>(&logPath4), SaveArg<1>(&logEntryOut),
195                         Return(true)));
196     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
197     EXPECT_FALSE(logPath4.empty());
198     EXPECT_NE(logEntryOut["Id"], nullptr);
199     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
200 
201     // Now send a LogEntry#5, we expect the LogEntry#3 to be deleted
202     std::string logPath5;
203     EXPECT_CALL(*mockFileWriterPtr, removeAll(logPath3)).WillOnce(Return(true));
204     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
205         .WillOnce(DoAll(SaveArg<0>(&logPath5), SaveArg<1>(&logEntryOut),
206                         Return(true)));
207     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
208     EXPECT_FALSE(logPath5.empty());
209     EXPECT_NE(logEntryOut["Id"], nullptr);
210     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
211 }
212 
TEST_F(ExternalStorerFileTest,OtherSchemaNoOdataIdTest)213 TEST_F(ExternalStorerFileTest, OtherSchemaNoOdataIdTest)
214 {
215     // Try a another PDRs without @odata.id.
216     std::string jsonStr = R"(
217       {
218         "@odata.type": "#MemoryMetrics.v1_4_1.MemoryMetrics",
219         "Id":"Metrics"
220       }
221     )";
222     EXPECT_THAT(exStorer->publishJson(jsonStr), false);
223 }
224 
TEST_F(ExternalStorerFileTest,OtherSchemaTypeTest)225 TEST_F(ExternalStorerFileTest, OtherSchemaTypeTest)
226 {
227     // A valid MemoryMetrics PDR.
228     std::string jsonStr = R"(
229       {
230         "@odata.id": "/redfish/v1/Systems/system/Memory/dimm0/MemoryMetrics",
231         "@odata.type": "#MemoryMetrics.v1_4_1.MemoryMetrics",
232         "Id": "Metrics"
233       }
234     )";
235     std::string exFolder =
236         "/some/path/redfish/v1/Systems/system/Memory/dimm0/MemoryMetrics";
237     nlohmann::json exJson = nlohmann::json::parse(jsonStr);
238     EXPECT_CALL(*mockFileWriterPtr, createFile(exFolder, exJson))
239         .WillOnce(Return(true));
240     EXPECT_THAT(exStorer->publishJson(jsonStr), true);
241 }
242 
243 } // namespace rde
244 } // namespace bios_bmc_smm_error_logger
245