xref: /openbmc/bios-bmc-smm-error-logger/test/rde_handler_test.cpp (revision e710a3677b03caf3be6a8c4c54c35652524f719e)
1 #include "nlohmann/json.hpp"
2 #include "rde/external_storer_interface.hpp"
3 #include "rde/rde_handler.hpp"
4 
5 #include <memory>
6 #include <span>
7 
8 #include <gmock/gmock-matchers.h>
9 #include <gmock/gmock.h>
10 #include <gtest/gtest.h>
11 
12 namespace bios_bmc_smm_error_logger
13 {
14 namespace rde
15 {
16 
17 using ::testing::_;
18 using ::testing::NiceMock;
19 using ::testing::Return;
20 
21 // Mock for ExternalStorerInterface
22 class MockExternalStorerInterface : public ExternalStorerInterface
23 {
24   public:
25     MOCK_METHOD(bool, publishJson, (std::string_view jsonStr), (override));
26 };
27 
28 // Test fixture for RdeCommandHandler
29 class RdeCommandHandlerTest : public ::testing::Test
30 {
31   protected:
32     std::unique_ptr<MockExternalStorerInterface> mockExStorerInstance;
33     // Note: RdeCommandHandler takes ownership of the raw pointer.
34     // We pass mockExStorerInstance.get() and it's moved into the handler.
35     // For safety in test setup, we'll create the handler with a moved
36     // unique_ptr.
37     std::unique_ptr<RdeCommandHandler> handler;
38 
SetUp()39     void SetUp() override
40     {
41         auto exStorer =
42             std::make_unique<NiceMock<MockExternalStorerInterface>>();
43         // Keep a raw pointer for EXPECT_CALL, but handler owns the unique_ptr
44         mockExStorer = exStorer.get();
45         handler = std::make_unique<RdeCommandHandler>(std::move(exStorer));
46     }
47 
48     // Helper to create RdeOperationInitReqHeader and its command data
createOpInitReqCmd(bool containsPayload,uint8_t opType,uint8_t sendDataTransferHandle,uint32_t resourceID,uint8_t opLocatorLength,uint16_t requestPayloadLength,const std::vector<uint8_t> & payloadData={})49     std::vector<uint8_t> createOpInitReqCmd(
50         bool containsPayload, uint8_t opType, uint8_t sendDataTransferHandle,
51         uint32_t resourceID, uint8_t opLocatorLength,
52         uint16_t requestPayloadLength,
53         const std::vector<uint8_t>& payloadData = {})
54     {
55         RdeOperationInitReqHeader header{};
56         header.containsRequestPayload = containsPayload;
57         header.operationType = opType;
58         header.sendDataTransferHandle = sendDataTransferHandle;
59         header.resourceID = resourceID;
60         header.operationLocatorLength = opLocatorLength;
61         header.requestPayloadLength = requestPayloadLength;
62 
63         std::vector<uint8_t> command(sizeof(header));
64         memcpy(command.data(), &header, sizeof(header));
65         command.insert(command.end(), payloadData.begin(), payloadData.end());
66         return command;
67     }
68 
69     // Helper to create MultipartReceiveResHeader and its command data
createMultiPartRespCmd(uint8_t transferFlag,uint32_t nextDataTransferHandleAsResourceId,uint16_t dataLength,const std::vector<uint8_t> & payloadData,const std::optional<uint32_t> & checksum=std::nullopt)70     std::vector<uint8_t> createMultiPartRespCmd(
71         uint8_t transferFlag, uint32_t nextDataTransferHandleAsResourceId,
72         uint16_t dataLength, const std::vector<uint8_t>& payloadData,
73         const std::optional<uint32_t>& checksum = std::nullopt)
74     {
75         MultipartReceiveResHeader header{};
76         header.transferFlag = transferFlag;
77         header.nextDataTransferHandle = nextDataTransferHandleAsResourceId;
78         header.dataLengthBytes = dataLength;
79 
80         std::vector<uint8_t> command(sizeof(header));
81         memcpy(command.data(), &header, sizeof(header));
82         command.insert(command.end(), payloadData.begin(), payloadData.end());
83 
84         if (checksum)
85         {
86             uint32_t csVal = *checksum;
87             command.push_back(static_cast<uint8_t>(csVal & 0xFF));
88             command.push_back(static_cast<uint8_t>((csVal >> 8) & 0xFF));
89             command.push_back(static_cast<uint8_t>((csVal >> 16) & 0xFF));
90             command.push_back(static_cast<uint8_t>((csVal >> 24) & 0xFF));
91         }
92 
93         return command;
94     }
95 
96     // To be used by EXPECT_CALL
97     MockExternalStorerInterface* mockExStorer;
98 };
99 
TEST_F(RdeCommandHandlerTest,DecodeRdeCommand_InvalidType)100 TEST_F(RdeCommandHandlerTest, DecodeRdeCommand_InvalidType)
101 {
102     std::vector<uint8_t> cmdData = {0x01, 0x02};
103     auto status =
104         handler->decodeRdeCommand(cmdData, static_cast<RdeCommandType>(0xFF));
105     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
106 }
107 
TEST_F(RdeCommandHandlerTest,GetDictionaryCount_Initial)108 TEST_F(RdeCommandHandlerTest, GetDictionaryCount_Initial)
109 {
110     EXPECT_EQ(handler->getDictionaryCount(), 0);
111 }
112 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_NoPayload)113 TEST_F(RdeCommandHandlerTest, OperationInitRequest_NoPayload)
114 {
115     auto cmd = createOpInitReqCmd(
116         false, // containsRequestPayload
117         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
118         1, 0, 0);
119     auto status =
120         handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
121     EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
122 }
123 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_CmdTooSmallForHeader)124 TEST_F(RdeCommandHandlerTest, OperationInitRequest_CmdTooSmallForHeader)
125 {
126     std::vector<uint8_t> cmdData = {0x01, 0x02}; // Smaller than header
127     auto status = handler->decodeRdeCommand(
128         cmdData, RdeCommandType::RdeOperationInitRequest);
129     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
130 }
131 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_CmdTooSmallForDeclaredPayload)132 TEST_F(RdeCommandHandlerTest,
133        OperationInitRequest_CmdTooSmallForDeclaredPayload)
134 {
135     // Header declares payload, but actual data is too short.
136     // Header: containsPayload=true, opLocatorLength=1, requestPayloadLength=5
137     // Actual payload data provided: only 1 byte for locator, 0 for payload.
138     RdeOperationInitReqHeader header{};
139     header.containsRequestPayload = true;
140     header.operationLocatorLength = 1;
141     header.requestPayloadLength = 5; // Expects 5 bytes of payload
142     std::vector<uint8_t> cmdData(sizeof(header));
143     memcpy(cmdData.data(), &header, sizeof(header));
144     cmdData.push_back(0xAA); // Only 1 byte for locator, payload missing
145     auto status = handler->decodeRdeCommand(
146         cmdData, RdeCommandType::RdeOperationInitRequest);
147     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
148 }
149 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_UnsupportedOperationType)150 TEST_F(RdeCommandHandlerTest, OperationInitRequest_UnsupportedOperationType)
151 {
152     auto cmd = createOpInitReqCmd(true, 0xFE, 0, 1, 0, 5, {1, 2, 3, 4, 5});
153     auto status =
154         handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
155     EXPECT_EQ(status, RdeDecodeStatus::RdeUnsupportedOperation);
156 }
157 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_PayloadOverflowNotSupported)158 TEST_F(RdeCommandHandlerTest, OperationInitRequest_PayloadOverflowNotSupported)
159 {
160     auto cmd = createOpInitReqCmd(
161         true,
162         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 1,
163         1, 0, 5, {1, 2, 3, 4, 5}); // sendDataTransferHandle != 0
164     auto status =
165         handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
166     EXPECT_EQ(status, RdeDecodeStatus::RdePayloadOverflow);
167 }
168 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_SchemaDictionaryNotFound)169 TEST_F(RdeCommandHandlerTest, OperationInitRequest_SchemaDictionaryNotFound)
170 {
171     std::vector<uint8_t> locatorAndPayload = {0x00}; // Minimal locator
172     auto cmd = createOpInitReqCmd(
173         true,
174         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
175         123, 1, 0, locatorAndPayload); // resourceID 123, opLocatorLength=1,
176                                        // payloadLength=0
177     auto status =
178         handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
179     EXPECT_EQ(status, RdeDecodeStatus::RdeNoDictionary);
180 }
181 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_AnnotationDictionaryNotFound)182 TEST_F(RdeCommandHandlerTest, OperationInitRequest_AnnotationDictionaryNotFound)
183 {
184     uint32_t schemaResourceId = 1;
185     std::vector<uint8_t> schemaDictData = {'s', 'c', 'h', 'e', 'm', 'a'};
186     uint32_t schemaChecksum = 0xb88e4152; // CRC32("schema")
187     auto cmdSchema = createMultiPartRespCmd(
188         static_cast<uint8_t>(
189             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
190         schemaResourceId, schemaDictData.size(), schemaDictData,
191         schemaChecksum);
192     ASSERT_EQ(handler->decodeRdeCommand(
193                   cmdSchema, RdeCommandType::RdeMultiPartReceiveResponse),
194               RdeDecodeStatus::RdeStopFlagReceived);
195 
196     std::vector<uint8_t> locatorAndPayload = {0x00};
197     auto cmdOpInit = createOpInitReqCmd(
198         true,
199         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
200         schemaResourceId, 1, 0, locatorAndPayload);
201     auto status = handler->decodeRdeCommand(
202         cmdOpInit, RdeCommandType::RdeOperationInitRequest);
203     EXPECT_EQ(status, RdeDecodeStatus::RdeNoDictionary);
204 }
205 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_BejDecodingError)206 TEST_F(RdeCommandHandlerTest, OperationInitRequest_BejDecodingError)
207 {
208     // Add dummy schema dictionary
209     uint32_t schemaResourceId = 1;
210     std::vector<uint8_t> schemaDictData = {'s', 'c', 'h', 'e', 'm', 'a'};
211     uint32_t schemaChecksum = 0xb88e4152; // CRC32("schema")
212     auto cmdSchema = createMultiPartRespCmd(
213         static_cast<uint8_t>(
214             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
215         schemaResourceId, schemaDictData.size(), schemaDictData,
216         schemaChecksum);
217     ASSERT_EQ(handler->decodeRdeCommand(
218                   cmdSchema, RdeCommandType::RdeMultiPartReceiveResponse),
219               RdeDecodeStatus::RdeStopFlagReceived);
220 
221     // Add dummy annotation dictionary
222     uint32_t annotationResourceId =
223         0; // DictionaryManager::annotationResourceId
224     std::vector<uint8_t> annotationDictData = {'a', 'n', 'n', 'o'};
225     uint32_t annotationChecksum = 0xc6e493b0; // CRC32("anno")
226     auto cmdAnnotation = createMultiPartRespCmd(
227         static_cast<uint8_t>(
228             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
229         annotationResourceId, annotationDictData.size(), annotationDictData,
230         annotationChecksum);
231     ASSERT_EQ(handler->decodeRdeCommand(
232                   cmdAnnotation, RdeCommandType::RdeMultiPartReceiveResponse),
233               RdeDecodeStatus::RdeStopFlagReceived);
234 
235     std::vector<uint8_t> locator = {0x00};
236     std::vector<uint8_t> bejPayload = {0x01, 0x02}; // Dummy BEJ payload
237     std::vector<uint8_t> opInitFullPayload = locator;
238     opInitFullPayload.insert(opInitFullPayload.end(), bejPayload.begin(),
239                              bejPayload.end());
240 
241     auto cmdOpInit = createOpInitReqCmd(
242         true,
243         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
244         schemaResourceId, locator.size(), bejPayload.size(), opInitFullPayload);
245 
246     // Expect BEJ decoding to fail with invalid dictionaries
247     EXPECT_CALL(*mockExStorer, publishJson(_)).Times(0);
248     auto status = handler->decodeRdeCommand(
249         cmdOpInit, RdeCommandType::RdeOperationInitRequest);
250     EXPECT_EQ(status, RdeDecodeStatus::RdeBejDecodingError);
251 }
252 
TEST_F(RdeCommandHandlerTest,OperationInitRequest_ExternalStorerPublishFails)253 TEST_F(RdeCommandHandlerTest, OperationInitRequest_ExternalStorerPublishFails)
254 {
255     // This test requires BejDecoder to succeed. Since we can't easily mock
256     // BejDecoder or provide universally valid simple BEJ dicts/payloads that
257     // guarantee success for the internal BejDecoder, this specific path is hard
258     // to test in isolation. We would need a known schema, annotation, and
259     // payload that successfully decodes.
260     GTEST_SKIP()
261         << "Skipping due to complexity of ensuring BEJ decode success without mock or valid complex BEJ data.";
262 }
263 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_CmdTooSmallForHeader)264 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_CmdTooSmallForHeader)
265 {
266     std::vector<uint8_t> cmdData = {0x01};
267     auto status = handler->decodeRdeCommand(
268         cmdData, RdeCommandType::RdeMultiPartReceiveResponse);
269     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
270 }
271 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_CmdTooSmallForDeclaredPayload)272 TEST_F(RdeCommandHandlerTest,
273        MultiPartReceiveResp_CmdTooSmallForDeclaredPayload)
274 {
275     MultipartReceiveResHeader header{};
276     header.transferFlag =
277         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart);
278     header.nextDataTransferHandle = 1;
279     header.dataLengthBytes = 10; // Expects 10 bytes
280 
281     std::vector<uint8_t> cmdData(sizeof(header));
282     memcpy(cmdData.data(), &header, sizeof(header));
283     cmdData.push_back(0xAA); // Only 1 byte of payload provided
284 
285     auto status = handler->decodeRdeCommand(
286         cmdData, RdeCommandType::RdeMultiPartReceiveResponse);
287     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
288 }
289 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_InvalidTransferFlag)290 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_InvalidTransferFlag)
291 {
292     std::vector<uint8_t> payload = {'d', 'a', 't', 'a'};
293     auto cmd = createMultiPartRespCmd(0xFF, 1, payload.size(),
294                                       payload); // Invalid flag
295     auto status = handler->decodeRdeCommand(
296         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
297     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
298 }
299 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagStart)300 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagStart)
301 {
302     uint32_t resourceId = 1;
303     std::vector<uint8_t> dataPayload = {'s', 't', 'a', 'r', 't'};
304     auto cmd = createMultiPartRespCmd(
305         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
306         resourceId, dataPayload.size(), dataPayload);
307     auto status = handler->decodeRdeCommand(
308         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
309     EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
310     EXPECT_EQ(handler->getDictionaryCount(), 0); // Not yet complete
311 }
312 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagMiddle_InvalidOrder)313 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagMiddle_InvalidOrder)
314 {
315     uint32_t resourceId = 1;
316     std::vector<uint8_t> dataPayload = {'m', 'i', 'd', 'd', 'l', 'e'};
317     auto cmd = createMultiPartRespCmd(
318         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
319         resourceId, dataPayload.size(), dataPayload);
320     auto status = handler->decodeRdeCommand(
321         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
322     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidPktOrder);
323 }
324 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagMiddle_AfterStart_SameResource)325 TEST_F(RdeCommandHandlerTest,
326        MultiPartReceiveResp_FlagMiddle_AfterStart_SameResource)
327 {
328     uint32_t resourceId = 1;
329     std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
330     auto cmdStart = createMultiPartRespCmd(
331         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
332         resourceId, startPayload.size(), startPayload);
333     ASSERT_EQ(handler->decodeRdeCommand(
334                   cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
335               RdeDecodeStatus::RdeOk);
336 
337     std::vector<uint8_t> middlePayload = {'m', 'i', 'd', 'd', 'l', 'e'};
338     auto cmdMiddle = createMultiPartRespCmd(
339         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
340         resourceId, middlePayload.size(), middlePayload);
341     auto status = handler->decodeRdeCommand(
342         cmdMiddle, RdeCommandType::RdeMultiPartReceiveResponse);
343     EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
344     EXPECT_EQ(handler->getDictionaryCount(), 0);
345 }
346 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagMiddle_AfterStart_NewResource)347 TEST_F(RdeCommandHandlerTest,
348        MultiPartReceiveResp_FlagMiddle_AfterStart_NewResource)
349 {
350     // Tests current behavior: if Middle flag comes for a new resource,
351     // previous resource is marked complete, new one is started. CRC continues.
352     uint32_t resourceId1 = 1;
353     std::vector<uint8_t> startPayload1 = {'r', '1', 's'};
354     auto cmdStart1 = createMultiPartRespCmd(
355         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
356         resourceId1, startPayload1.size(), startPayload1);
357     ASSERT_EQ(handler->decodeRdeCommand(
358                   cmdStart1, RdeCommandType::RdeMultiPartReceiveResponse),
359               RdeDecodeStatus::RdeOk);
360 
361     uint32_t resourceId2 = 2;
362     std::vector<uint8_t> middlePayload2 = {'r', '2', 'm'};
363     auto cmdMiddle2 = createMultiPartRespCmd(
364         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
365         resourceId2, middlePayload2.size(), middlePayload2);
366     auto status = handler->decodeRdeCommand(
367         cmdMiddle2, RdeCommandType::RdeMultiPartReceiveResponse);
368     EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
369     EXPECT_EQ(handler->getDictionaryCount(), 1); // Resource 1 completed
370 }
371 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagEnd_InvalidOrder)372 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagEnd_InvalidOrder)
373 {
374     uint32_t resourceId = 1;
375     std::vector<uint8_t> dataPayload = {'e', 'n', 'd'};
376     uint32_t checksum = 0xfc33b1; // CRC32("end")
377     auto cmd = createMultiPartRespCmd(
378         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
379         resourceId, dataPayload.size(), dataPayload, checksum);
380     auto status = handler->decodeRdeCommand(
381         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
382     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidPktOrder);
383 }
384 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_ValidChecksum)385 TEST_F(RdeCommandHandlerTest,
386        MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_ValidChecksum)
387 {
388     uint32_t resourceId = 1;
389     std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
390     auto cmdStart = createMultiPartRespCmd(
391         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
392         resourceId, startPayload.size(), startPayload);
393     ASSERT_EQ(handler->decodeRdeCommand(
394                   cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
395               RdeDecodeStatus::RdeOk);
396 
397     std::vector<uint8_t> endPayload = {'e', 'n', 'd'};
398     uint32_t checksum = 0x4800f1a; // CRC32("startend")
399     auto cmdEnd = createMultiPartRespCmd(
400         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
401         resourceId, endPayload.size(), endPayload, checksum);
402     auto status = handler->decodeRdeCommand(
403         cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
404     EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
405     EXPECT_EQ(handler->getDictionaryCount(), 1);
406 }
407 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_InvalidChecksum)408 TEST_F(RdeCommandHandlerTest,
409        MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_InvalidChecksum)
410 {
411     uint32_t resourceId = 1;
412     std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
413     auto cmdStart = createMultiPartRespCmd(
414         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
415         resourceId, startPayload.size(), startPayload);
416     ASSERT_EQ(handler->decodeRdeCommand(
417                   cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
418               RdeDecodeStatus::RdeOk);
419 
420     std::vector<uint8_t> endPayload = {'e', 'n', 'd'};
421     uint32_t invalidChecksum = 0x12345678; // Invalid checksum
422     auto cmdEnd = createMultiPartRespCmd(
423         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
424         resourceId, endPayload.size(), endPayload, invalidChecksum);
425     auto status = handler->decodeRdeCommand(
426         cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
427     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
428     EXPECT_EQ(handler->getDictionaryCount(), 0); // Dictionaries invalidated
429 }
430 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagEnd_AfterStart_NewResource_UsesPrevCrcState)431 TEST_F(RdeCommandHandlerTest,
432        MultiPartReceiveResp_FlagEnd_AfterStart_NewResource_UsesPrevCrcState)
433 {
434     // This test verifies that if an End flag for a new resource follows a Start
435     // flag for a different resource, the CRC calculation for the new resource's
436     // data incorrectly continues from the previous resource's CRC state.
437     uint32_t resourceId1 = 1;
438     std::vector<uint8_t> startPayload1 = {'r', '1', 's'}; // CRC for "r1s"
439     auto cmdStart1 = createMultiPartRespCmd(
440         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
441         resourceId1, startPayload1.size(), startPayload1);
442     ASSERT_EQ(handler->decodeRdeCommand(
443                   cmdStart1, RdeCommandType::RdeMultiPartReceiveResponse),
444               RdeDecodeStatus::RdeOk);
445 
446     uint32_t resourceId2 = 2;
447     std::vector<uint8_t> endPayload2 = {'r', '2', 'e'};
448     // Checksum for "r2e" ALONE is 0x789ca48a.
449     // If CRC continued from "r1s", this checksum will be wrong.
450     uint32_t checksumForR2eAlone = 0x789ca48a;
451     auto cmdEnd2 = createMultiPartRespCmd(
452         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
453         resourceId2, endPayload2.size(), endPayload2, checksumForR2eAlone);
454 
455     auto status = handler->decodeRdeCommand(
456         cmdEnd2, RdeCommandType::RdeMultiPartReceiveResponse);
457     // Expect InvalidChecksum because internal CRC is for "r1s" + "r2e"
458     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
459     EXPECT_EQ(handler->getDictionaryCount(), 0); // Dictionaries invalidated
460 }
461 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagStartAndEnd_ValidChecksum)462 TEST_F(RdeCommandHandlerTest,
463        MultiPartReceiveResp_FlagStartAndEnd_ValidChecksum)
464 {
465     uint32_t resourceId = 1;
466     std::vector<uint8_t> dataPayload = {'c', 'o', 'm', 'p', 'l', 'e', 't', 'e'};
467     uint32_t checksum = 0x4267d023; // CRC32("complete")
468     auto cmd = createMultiPartRespCmd(
469         static_cast<uint8_t>(
470             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
471         resourceId, dataPayload.size(), dataPayload, checksum);
472     auto status = handler->decodeRdeCommand(
473         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
474     EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
475     EXPECT_EQ(handler->getDictionaryCount(), 1);
476 }
477 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_FlagStartAndEnd_InvalidChecksum)478 TEST_F(RdeCommandHandlerTest,
479        MultiPartReceiveResp_FlagStartAndEnd_InvalidChecksum)
480 {
481     uint32_t resourceId = 1;
482     std::vector<uint8_t> dataPayload = {'c', 'o', 'm', 'p', 'l', 'e', 't', 'e'};
483     uint32_t invalidChecksum = 0x12345678; // Invalid checksum
484     auto cmd = createMultiPartRespCmd(
485         static_cast<uint8_t>(
486             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
487         resourceId, dataPayload.size(), dataPayload, invalidChecksum);
488     auto status = handler->decodeRdeCommand(
489         cmd, RdeCommandType::RdeMultiPartReceiveResponse);
490     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
491     EXPECT_EQ(handler->getDictionaryCount(), 0);
492 }
493 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_Sequence_StartMiddleEnd_Valid)494 TEST_F(RdeCommandHandlerTest,
495        MultiPartReceiveResp_Sequence_StartMiddleEnd_Valid)
496 {
497     uint32_t resourceId = 42;
498 
499     std::vector<uint8_t> startPayload = {'p', 'a', 'r', 't', '1'};
500     auto cmdStart = createMultiPartRespCmd(
501         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
502         resourceId, startPayload.size(), startPayload);
503     ASSERT_EQ(handler->decodeRdeCommand(
504                   cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
505               RdeDecodeStatus::RdeOk);
506 
507     std::vector<uint8_t> middlePayload = {'p', 'a', 'r', 't', '2'};
508     auto cmdMiddle = createMultiPartRespCmd(
509         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
510         resourceId, middlePayload.size(), middlePayload);
511     ASSERT_EQ(handler->decodeRdeCommand(
512                   cmdMiddle, RdeCommandType::RdeMultiPartReceiveResponse),
513               RdeDecodeStatus::RdeOk);
514 
515     std::vector<uint8_t> endPayload = {'p', 'a', 'r', 't', '3'};
516     uint32_t checksum = 0xf5295f3; // CRC32("part1part2part3")
517     auto cmdEnd = createMultiPartRespCmd(
518         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
519         resourceId, endPayload.size(), endPayload, checksum);
520     auto status = handler->decodeRdeCommand(
521         cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
522     EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
523     EXPECT_EQ(handler->getDictionaryCount(), 1);
524 }
525 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_MultipleDictionaries_ValidSequence)526 TEST_F(RdeCommandHandlerTest,
527        MultiPartReceiveResp_MultipleDictionaries_ValidSequence)
528 {
529     // Dictionary 1: StartAndEnd
530     uint32_t resourceId1 = 1;
531     std::vector<uint8_t> payload1 = {'d', 'i', 'c', 't', '1'};
532     uint32_t checksum1 = 0xbca257a8; // CRC32("dict1")
533     auto cmd1 = createMultiPartRespCmd(
534         static_cast<uint8_t>(
535             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
536         resourceId1, payload1.size(), payload1, checksum1);
537     ASSERT_EQ(handler->decodeRdeCommand(
538                   cmd1, RdeCommandType::RdeMultiPartReceiveResponse),
539               RdeDecodeStatus::RdeStopFlagReceived);
540     ASSERT_EQ(handler->getDictionaryCount(), 1);
541 
542     // Dictionary 2: Start, Middle, End
543     uint32_t resourceId2 = 2;
544     std::vector<uint8_t> startPayload2 = {'d', '2', '_'};
545     auto cmdStart2 = createMultiPartRespCmd(
546         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
547         resourceId2, startPayload2.size(), startPayload2);
548     ASSERT_EQ(handler->decodeRdeCommand(
549                   cmdStart2, RdeCommandType::RdeMultiPartReceiveResponse),
550               RdeDecodeStatus::RdeOk);
551     ASSERT_EQ(handler->getDictionaryCount(),
552               1); // Dict1 still valid, Dict2 not yet
553 
554     std::vector<uint8_t> middlePayload2 = {'m', 'i', 'd'};
555     auto cmdMiddle2 = createMultiPartRespCmd(
556         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
557         resourceId2, middlePayload2.size(), middlePayload2);
558     ASSERT_EQ(handler->decodeRdeCommand(
559                   cmdMiddle2, RdeCommandType::RdeMultiPartReceiveResponse),
560               RdeDecodeStatus::RdeOk);
561 
562     std::vector<uint8_t> endPayload2 = {'e', 'n', 'd'};
563     uint32_t checksum2 = 0x9e428a17; // CRC32("d2_midend")
564     auto cmdEnd2 = createMultiPartRespCmd(
565         static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
566         resourceId2, endPayload2.size(), endPayload2, checksum2);
567     ASSERT_EQ(handler->decodeRdeCommand(
568                   cmdEnd2, RdeCommandType::RdeMultiPartReceiveResponse),
569               RdeDecodeStatus::RdeStopFlagReceived);
570     ASSERT_EQ(handler->getDictionaryCount(),
571               2); // Both dictionaries should now be valid
572 }
573 
TEST_F(RdeCommandHandlerTest,MultiPartReceiveResp_HandleCrc_MismatchedSize)574 TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_HandleCrc_MismatchedSize)
575 {
576     // Header will claim 10 bytes of data.
577     MultipartReceiveResHeader header{};
578     header.transferFlag = static_cast<uint8_t>(
579         RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd);
580     header.nextDataTransferHandle = 1; // dummy resource ID
581     header.dataLengthBytes = 10;
582 
583     // Create a command that is exactly the size of the header + data, which
584     // means it is missing the 4-byte checksum. This will pass the initial size
585     // check but fail the one in handleCrc.
586     size_t actualSize =
587         sizeof(MultipartReceiveResHeader) + header.dataLengthBytes;
588     std::vector<uint8_t> command(actualSize);
589     memcpy(command.data(), &header, sizeof(header));
590 
591     auto status = handler->decodeRdeCommand(
592         command, RdeCommandType::RdeMultiPartReceiveResponse);
593     EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
594 }
595 
596 /**
597  * @brief Dummy values for annotation dictionary. We do not need the annotation
598  * dictionary. So this contains a dictionary with some dummy values. But the RDE
599  * header is correct.
600  */
601 constexpr std::array<uint8_t, 38> mRcvDummyAnnotation{
602     {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
603      0x0,  0x0,  0xc,  0x0,  0x0,  0xf0, 0xf0, 0xf1, 0x18, 0x00,
604      0x0,  0x0,  0x0,  0x0,  0x0,  0x16, 0x0,  0x5,  0x0,  0xc,
605      0x84, 0x0,  0x14, 0x0,  0xe2, 0x14, 0xd2, 0x0b}};
606 
607 constexpr std::array<uint8_t, 38> mRcvDummyInvalidChecksum{
608     {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
609      0x0,  0x0,  0xc,  0x0,  0x0,  0xf0, 0xf0, 0xf1, 0x17, 0x1,
610      0x0,  0x0,  0x0,  0x0,  0x0,  0x16, 0x0,  0x5,  0x0,  0xc,
611      0x84, 0x0,  0x14, 0x0,  0x17, 0x86, 0x00, 0x00}};
612 
613 /**
614  * @brief MultipartReceive command with START_AND_END flag set.
615  */
616 constexpr std::array<uint8_t, 293> mRcvInput0StartAndEnd{
617     {0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x0,  0x0,
618      0xc,  0x0,  0x0,  0xf0, 0xf0, 0xf1, 0x17, 0x1,  0x0,  0x0,  0x0,  0x0,
619      0x0,  0x16, 0x0,  0x5,  0x0,  0xc,  0x84, 0x0,  0x14, 0x0,  0x0,  0x48,
620      0x0,  0x1,  0x0,  0x13, 0x90, 0x0,  0x56, 0x1,  0x0,  0x0,  0x0,  0x0,
621      0x0,  0x3,  0xa3, 0x0,  0x74, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16,
622      0xa6, 0x0,  0x34, 0x3,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16, 0xbc, 0x0,
623      0x64, 0x4,  0x0,  0x0,  0x0,  0x0,  0x0,  0x13, 0xd2, 0x0,  0x0,  0x0,
624      0x0,  0x52, 0x0,  0x2,  0x0,  0x0,  0x0,  0x0,  0x74, 0x0,  0x0,  0x0,
625      0x0,  0x0,  0x0,  0xf,  0xe5, 0x0,  0x46, 0x1,  0x0,  0x66, 0x0,  0x3,
626      0x0,  0xb,  0xf4, 0x0,  0x50, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x9,
627      0xff, 0x0,  0x50, 0x1,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0x8,  0x1,
628      0x50, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0xf,  0x1,  0x44, 0x75,
629      0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0,  0x43, 0x68,
630      0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x70,
631      0x65, 0x72, 0x74, 0x79, 0x0,  0x49, 0x64, 0x0,  0x53, 0x61, 0x6d, 0x70,
632      0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
633      0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
634      0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
635      0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
636      0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x41,
637      0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
638      0x6e, 0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
639      0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0,  0x4c, 0x69,
640      0x6e, 0x6b, 0x55, 0x70, 0x0,  0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
641      0x0,  0x8c, 0x87, 0xed, 0x74}};
642 
643 /**
644  * @brief MultipartReceive command with START flag set.
645  */
646 constexpr std::array<uint8_t, 166> mRcvInput1Start{
647     {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x0,  0x0,
648      0xc,  0x0,  0x0,  0xf0, 0xf0, 0xf1, 0x17, 0x1,  0x0,  0x0,  0x0,  0x0,
649      0x0,  0x16, 0x0,  0x5,  0x0,  0xc,  0x84, 0x0,  0x14, 0x0,  0x0,  0x48,
650      0x0,  0x1,  0x0,  0x13, 0x90, 0x0,  0x56, 0x1,  0x0,  0x0,  0x0,  0x0,
651      0x0,  0x3,  0xa3, 0x0,  0x74, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16,
652      0xa6, 0x0,  0x34, 0x3,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16, 0xbc, 0x0,
653      0x64, 0x4,  0x0,  0x0,  0x0,  0x0,  0x0,  0x13, 0xd2, 0x0,  0x0,  0x0,
654      0x0,  0x52, 0x0,  0x2,  0x0,  0x0,  0x0,  0x0,  0x74, 0x0,  0x0,  0x0,
655      0x0,  0x0,  0x0,  0xf,  0xe5, 0x0,  0x46, 0x1,  0x0,  0x66, 0x0,  0x3,
656      0x0,  0xb,  0xf4, 0x0,  0x50, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x9,
657      0xff, 0x0,  0x50, 0x1,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0x8,  0x1,
658      0x50, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0xf,  0x1,  0x44, 0x75,
659      0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0,  0x43, 0x68,
660      0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72}};
661 
662 /**
663  * @brief MultipartReceive command with END flag set.
664  */
665 constexpr std::array<uint8_t, 137> mRcvInput1End{
666     {0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x6f, 0x70,
667      0x65, 0x72, 0x74, 0x79, 0x0,  0x49, 0x64, 0x0,  0x53, 0x61, 0x6d, 0x70,
668      0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
669      0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
670      0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
671      0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
672      0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x41,
673      0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
674      0x6e, 0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
675      0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0,  0x4c, 0x69,
676      0x6e, 0x6b, 0x55, 0x70, 0x0,  0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
677      0x0,  0x8c, 0x87, 0xed, 0x74}};
678 
679 /**
680  * @brief MultipartReceive command with START flag set.
681  */
682 constexpr std::array<uint8_t, 106> mRcvInput2Start{
683     {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x0,  0x00, 0x00, 0x0,  0x0,
684      0xc,  0x0,  0x0,  0xf0, 0xf0, 0xf1, 0x17, 0x1,  0x0,  0x0,  0x0,  0x0,
685      0x0,  0x16, 0x0,  0x5,  0x0,  0xc,  0x84, 0x0,  0x14, 0x0,  0x0,  0x48,
686      0x0,  0x1,  0x0,  0x13, 0x90, 0x0,  0x56, 0x1,  0x0,  0x0,  0x0,  0x0,
687      0x0,  0x3,  0xa3, 0x0,  0x74, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16,
688      0xa6, 0x0,  0x34, 0x3,  0x0,  0x0,  0x0,  0x0,  0x0,  0x16, 0xbc, 0x0,
689      0x64, 0x4,  0x0,  0x0,  0x0,  0x0,  0x0,  0x13, 0xd2, 0x0,  0x0,  0x0,
690      0x0,  0x52, 0x0,  0x2,  0x0,  0x0,  0x0,  0x0,  0x74, 0x0,  0x0,  0x0,
691      0x0,  0x0,  0x0,  0xf,  0xe5, 0x0,  0x46, 0x1,  0x0,  0x66}};
692 
693 /**
694  * @brief MultipartReceive command with MIDDLE flag set.
695  */
696 constexpr std::array<uint8_t, 106> mRcvInput2Mid{
697     {0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x60, 0x0,  0x00, 0x00, 0x0,  0x3,
698      0x0,  0xb,  0xf4, 0x0,  0x50, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x9,
699      0xff, 0x0,  0x50, 0x1,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0x8,  0x1,
700      0x50, 0x2,  0x0,  0x0,  0x0,  0x0,  0x0,  0x7,  0xf,  0x1,  0x44, 0x75,
701      0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0,  0x43, 0x68,
702      0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x70,
703      0x65, 0x72, 0x74, 0x79, 0x0,  0x49, 0x64, 0x0,  0x53, 0x61, 0x6d, 0x70,
704      0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
705      0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70}};
706 
707 /**
708  * @brief MultipartReceive command with END flag set.
709  */
710 constexpr std::array<uint8_t, 101> mRcvInput2End{
711     {0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x57, 0x0,  0x00, 0x00, 0x6c, 0x65,
712      0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
713      0x72, 0x74, 0x79, 0x0,  0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
714      0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0,  0x41,
715      0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
716      0x6e, 0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
717      0x0,  0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0,  0x4c, 0x69,
718      0x6e, 0x6b, 0x55, 0x70, 0x0,  0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
719      0x0,  0x8c, 0x87, 0xed, 0x74}};
720 
721 /**
722  * @brief RDEOperationInit command with encoded json/dummysimple.json as the
723  * payload.
724  */
725 constexpr std::array<uint8_t, 113> mInitOp{
726     {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00,
727      0x00, 0x60, 0x00, 0x00, 0x00, 0x0,  0xf0, 0xf0, 0xf1, 0x0,  0x0,  0x0,
728      0x1,  0x0,  0x0,  0x1,  0x54, 0x1,  0x5,  0x1,  0x2,  0x50, 0x1,  0x9,
729      0x44, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x49, 0x44, 0x0,  0x1,  0x6,  0x20,
730      0x1,  0x0,  0x1,  0x8,  0x60, 0x1,  0xb,  0x1,  0x2,  0x38, 0xea, 0x1,
731      0x0,  0x2,  0xa3, 0x23, 0x1,  0x0,  0x1,  0x4,  0x70, 0x1,  0x1,  0x0,
732      0x1,  0x0,  0x10, 0x1,  0x24, 0x1,  0x2,  0x1,  0x0,  0x0,  0x1,  0xf,
733      0x1,  0x2,  0x1,  0x0,  0x70, 0x1,  0x1,  0x1,  0x1,  0x2,  0x40, 0x1,
734      0x2,  0x1,  0x2,  0x1,  0x2,  0x0,  0x1,  0x9,  0x1,  0x1,  0x1,  0x2,
735      0x40, 0x1,  0x2,  0x1,  0x2}};
736 
737 class MockExternalStorer : public ExternalStorerInterface
738 {
739   public:
740     MOCK_METHOD(bool, publishJson, (std::string_view jsonStr), (override));
741 };
742 
743 class RdeHandlerTest : public ::testing::Test
744 {
745   public:
RdeHandlerTest()746     RdeHandlerTest() : mockExStorer(std::make_unique<MockExternalStorer>())
747     {
748         mockExStorerPtr = dynamic_cast<MockExternalStorer*>(mockExStorer.get());
749         rdeH = std::make_unique<RdeCommandHandler>(std::move(mockExStorer));
750     }
751 
752   protected:
753     std::unique_ptr<ExternalStorerInterface> mockExStorer;
754     std::unique_ptr<RdeCommandHandler> rdeH;
755     MockExternalStorer* mockExStorerPtr;
756     const std::string exJson =
757         R"({"Id":"Dummy ID","SampleIntegerProperty":null,"SampleRealProperty":-5576.9123,"SampleEnabledProperty":false,"ChildArrayProperty":[{"AnotherBoolean":true,"LinkStatus":"NoLink"},{"LinkStatus":"NoLink"}]})";
758 };
759 
TEST_F(RdeHandlerTest,DictionaryStartAndEndTest)760 TEST_F(RdeHandlerTest, DictionaryStartAndEndTest)
761 {
762     // Send a payload with START_AND_END flag.
763     EXPECT_THAT(
764         rdeH->decodeRdeCommand(std::span(mRcvInput0StartAndEnd),
765                                RdeCommandType::RdeMultiPartReceiveResponse),
766         RdeDecodeStatus::RdeStopFlagReceived);
767     EXPECT_THAT(rdeH->getDictionaryCount(), 1);
768     // Send annotation dictionary.
769     EXPECT_THAT(
770         rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
771                                RdeCommandType::RdeMultiPartReceiveResponse),
772         RdeDecodeStatus::RdeStopFlagReceived);
773     EXPECT_THAT(rdeH->getDictionaryCount(), 2);
774 
775     // Send the encoded payload.
776     EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
777     EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
778                                        RdeCommandType::RdeOperationInitRequest),
779                 RdeDecodeStatus::RdeOk);
780 }
781 
TEST_F(RdeHandlerTest,DictionaryStartThenEndTest)782 TEST_F(RdeHandlerTest, DictionaryStartThenEndTest)
783 {
784     // Send a payload with START flag.
785     EXPECT_THAT(
786         rdeH->decodeRdeCommand(std::span(mRcvInput1Start),
787                                RdeCommandType::RdeMultiPartReceiveResponse),
788         RdeDecodeStatus::RdeOk);
789     // We didn't send END. So dictionary count should be 0.
790     EXPECT_THAT(rdeH->getDictionaryCount(), 0);
791     // Send a payload with END flag.
792     EXPECT_THAT(
793         rdeH->decodeRdeCommand(std::span(mRcvInput1End),
794                                RdeCommandType::RdeMultiPartReceiveResponse),
795         RdeDecodeStatus::RdeStopFlagReceived);
796     EXPECT_THAT(rdeH->getDictionaryCount(), 1);
797     // Send annotation dictionary.
798     EXPECT_THAT(
799         rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
800                                RdeCommandType::RdeMultiPartReceiveResponse),
801         RdeDecodeStatus::RdeStopFlagReceived);
802     EXPECT_THAT(rdeH->getDictionaryCount(), 2);
803 
804     // Send the encoded payload.
805     EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
806     EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
807                                        RdeCommandType::RdeOperationInitRequest),
808                 RdeDecodeStatus::RdeOk);
809 
810     // Sending the START again for same resource ID should decrease the
811     // dictionary count.
812     EXPECT_THAT(
813         rdeH->decodeRdeCommand(std::span(mRcvInput1Start),
814                                RdeCommandType::RdeMultiPartReceiveResponse),
815         RdeDecodeStatus::RdeOk);
816     EXPECT_THAT(rdeH->getDictionaryCount(), 1);
817 }
818 
TEST_F(RdeHandlerTest,DictionaryStartMidEndTest)819 TEST_F(RdeHandlerTest, DictionaryStartMidEndTest)
820 {
821     // Send a payload with START flag.
822     EXPECT_THAT(
823         rdeH->decodeRdeCommand(std::span(mRcvInput2Start),
824                                RdeCommandType::RdeMultiPartReceiveResponse),
825         RdeDecodeStatus::RdeOk);
826     // We didn't send END. So dictionary count should be 0.
827     EXPECT_THAT(rdeH->getDictionaryCount(), 0);
828     // Send a payload with MIDDLE flag.
829     EXPECT_THAT(
830         rdeH->decodeRdeCommand(std::span(mRcvInput2Mid),
831                                RdeCommandType::RdeMultiPartReceiveResponse),
832         RdeDecodeStatus::RdeOk);
833     // We didn't send END. So dictionary count should be 0.
834     EXPECT_THAT(rdeH->getDictionaryCount(), 0);
835     // Send a payload with END flag.
836     EXPECT_THAT(
837         rdeH->decodeRdeCommand(std::span(mRcvInput2End),
838                                RdeCommandType::RdeMultiPartReceiveResponse),
839         RdeDecodeStatus::RdeStopFlagReceived);
840     EXPECT_THAT(rdeH->getDictionaryCount(), 1);
841 
842     // Send annotation dictionary.
843     EXPECT_THAT(
844         rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
845                                RdeCommandType::RdeMultiPartReceiveResponse),
846         RdeDecodeStatus::RdeStopFlagReceived);
847     EXPECT_THAT(rdeH->getDictionaryCount(), 2);
848 
849     // Send the encoded payload.
850     EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
851     EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
852                                        RdeCommandType::RdeOperationInitRequest),
853                 RdeDecodeStatus::RdeOk);
854 }
855 
TEST_F(RdeHandlerTest,InvalidDictionaryFlowTest)856 TEST_F(RdeHandlerTest, InvalidDictionaryFlowTest)
857 {
858     // Send a payload with MIDDLE flag before START and it should fail.
859     EXPECT_THAT(
860         rdeH->decodeRdeCommand(std::span(mRcvInput2Mid),
861                                RdeCommandType::RdeMultiPartReceiveResponse),
862         RdeDecodeStatus::RdeInvalidPktOrder);
863     // Send a payload with END flag before START and it should fail.
864     EXPECT_THAT(
865         rdeH->decodeRdeCommand(std::span(mRcvInput2End),
866                                RdeCommandType::RdeMultiPartReceiveResponse),
867         RdeDecodeStatus::RdeInvalidPktOrder);
868 }
869 
TEST_F(RdeHandlerTest,MissingDictionaryTest)870 TEST_F(RdeHandlerTest, MissingDictionaryTest)
871 {
872     // Try decoding without any dictionaries.
873     EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
874                                        RdeCommandType::RdeOperationInitRequest),
875                 RdeDecodeStatus::RdeNoDictionary);
876 
877     // Try decoding just with annotation dictionary.
878     EXPECT_THAT(
879         rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
880                                RdeCommandType::RdeMultiPartReceiveResponse),
881         RdeDecodeStatus::RdeStopFlagReceived);
882     EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
883                                        RdeCommandType::RdeOperationInitRequest),
884                 RdeDecodeStatus::RdeNoDictionary);
885 }
886 
TEST_F(RdeHandlerTest,InvalidDictionaryChecksumTest)887 TEST_F(RdeHandlerTest, InvalidDictionaryChecksumTest)
888 {
889     // Send a dictionary with an invalid checksum.
890     EXPECT_THAT(
891         rdeH->decodeRdeCommand(std::span(mRcvDummyInvalidChecksum),
892                                RdeCommandType::RdeMultiPartReceiveResponse),
893         RdeDecodeStatus::RdeInvalidChecksum);
894 }
895 
896 } // namespace rde
897 } // namespace bios_bmc_smm_error_logger
898