@@ -203,3 +203,276 @@ TEST(DataSourceEventHandlerTests, HandlesDeleteSegment) {
203203
204204 ASSERT_FALSE (store->GetSegment (" segmentA" )->item );
205205}
206+
207+ TEST (DataSourceEventHandlerTests, HandlesPatchWithNullDataForFlag) {
208+ auto logger = launchdarkly::logging::NullLogger ();
209+ auto store = std::make_shared<MemoryStore>();
210+ DataSourceStatusManager manager;
211+ DataSourceEventHandler event_handler (*store, logger, manager);
212+
213+ // Initialize the store
214+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
215+
216+ // Null data should be treated as invalid, not crash the application
217+ auto res = event_handler.HandleMessage (
218+ " patch" , R"( {"path": "/flags/flagA", "data": null})" );
219+
220+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
221+ // The error should be recorded, but we stay in Valid state after a previous successful PUT
222+ EXPECT_EQ (DataSourceStatus::DataSourceState::kValid ,
223+ manager.Status ().State ());
224+ ASSERT_TRUE (manager.Status ().LastError ().has_value ());
225+ EXPECT_EQ (DataSourceStatus::ErrorInfo::ErrorKind::kInvalidData ,
226+ manager.Status ().LastError ()->Kind ());
227+ }
228+
229+ TEST (DataSourceEventHandlerTests, HandlesPatchWithNullDataForSegment) {
230+ auto logger = launchdarkly::logging::NullLogger ();
231+ auto store = std::make_shared<MemoryStore>();
232+ DataSourceStatusManager manager;
233+ DataSourceEventHandler event_handler (*store, logger, manager);
234+
235+ // Initialize the store
236+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
237+
238+ // Null data should be treated as invalid, not crash the application
239+ auto res = event_handler.HandleMessage (
240+ " patch" , R"( {"path": "/segments/segmentA", "data": null})" );
241+
242+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
243+ // The error should be recorded, but we stay in Valid state after a previous successful PUT
244+ EXPECT_EQ (DataSourceStatus::DataSourceState::kValid ,
245+ manager.Status ().State ());
246+ ASSERT_TRUE (manager.Status ().LastError ().has_value ());
247+ EXPECT_EQ (DataSourceStatus::ErrorInfo::ErrorKind::kInvalidData ,
248+ manager.Status ().LastError ()->Kind ());
249+ }
250+
251+ TEST (DataSourceEventHandlerTests, HandlesPatchWithMissingDataField) {
252+ auto logger = launchdarkly::logging::NullLogger ();
253+ auto store = std::make_shared<MemoryStore>();
254+ DataSourceStatusManager manager;
255+ DataSourceEventHandler event_handler (*store, logger, manager);
256+
257+ // Initialize the store
258+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
259+
260+ // Missing data field should also be treated as invalid
261+ auto res = event_handler.HandleMessage (
262+ " patch" , R"( {"path": "/flags/flagA"})" );
263+
264+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
265+ }
266+
267+ TEST (DataSourceEventHandlerTests, HandlesPutWithNullData) {
268+ auto logger = launchdarkly::logging::NullLogger ();
269+ auto store = std::make_shared<MemoryStore>();
270+ DataSourceStatusManager manager;
271+ DataSourceEventHandler event_handler (*store, logger, manager);
272+
273+ // PUT with null data should also be handled safely
274+ auto res = event_handler.HandleMessage (
275+ " put" , R"( {"path":"/", "data": null})" );
276+
277+ // PUT handles this differently - it may succeed with empty data
278+ // The important thing is it doesn't crash
279+ ASSERT_TRUE (res == DataSourceEventHandler::MessageStatus::kMessageHandled ||
280+ res == DataSourceEventHandler::MessageStatus::kInvalidMessage );
281+ }
282+
283+ // Tests for wrong data types (schema validation errors)
284+
285+ TEST (DataSourceEventHandlerTests, HandlesPatchWithBooleanData) {
286+ auto logger = launchdarkly::logging::NullLogger ();
287+ auto store = std::make_shared<MemoryStore>();
288+ DataSourceStatusManager manager;
289+ DataSourceEventHandler event_handler (*store, logger, manager);
290+
291+ // Initialize the store
292+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
293+
294+ // Boolean data instead of object should be treated as invalid
295+ auto res = event_handler.HandleMessage (
296+ " patch" , R"( {"path": "/flags/flagA", "data": true})" );
297+
298+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
299+ }
300+
301+ TEST (DataSourceEventHandlerTests, HandlesPatchWithStringData) {
302+ auto logger = launchdarkly::logging::NullLogger ();
303+ auto store = std::make_shared<MemoryStore>();
304+ DataSourceStatusManager manager;
305+ DataSourceEventHandler event_handler (*store, logger, manager);
306+
307+ // Initialize the store
308+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
309+
310+ // String data instead of object should be treated as invalid
311+ auto res = event_handler.HandleMessage (
312+ " patch" , R"( {"path": "/flags/flagA", "data": "not an object"})" );
313+
314+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
315+ }
316+
317+ TEST (DataSourceEventHandlerTests, HandlesPatchWithArrayData) {
318+ auto logger = launchdarkly::logging::NullLogger ();
319+ auto store = std::make_shared<MemoryStore>();
320+ DataSourceStatusManager manager;
321+ DataSourceEventHandler event_handler (*store, logger, manager);
322+
323+ // Initialize the store
324+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
325+
326+ // Array data instead of object should be treated as invalid
327+ auto res = event_handler.HandleMessage (
328+ " patch" , R"( {"path": "/flags/flagA", "data": []})" );
329+
330+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
331+ }
332+
333+ TEST (DataSourceEventHandlerTests, HandlesPatchWithNumberData) {
334+ auto logger = launchdarkly::logging::NullLogger ();
335+ auto store = std::make_shared<MemoryStore>();
336+ DataSourceStatusManager manager;
337+ DataSourceEventHandler event_handler (*store, logger, manager);
338+
339+ // Initialize the store
340+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
341+
342+ // Number data instead of object should be treated as invalid
343+ auto res = event_handler.HandleMessage (
344+ " patch" , R"( {"path": "/flags/flagA", "data": 42})" );
345+
346+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
347+ }
348+
349+ TEST (DataSourceEventHandlerTests, HandlesDeleteWithStringVersion) {
350+ auto logger = launchdarkly::logging::NullLogger ();
351+ auto store = std::make_shared<MemoryStore>();
352+ DataSourceStatusManager manager;
353+ DataSourceEventHandler event_handler (*store, logger, manager);
354+
355+ // Initialize the store
356+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
357+
358+ // String version instead of number should be treated as invalid
359+ auto res = event_handler.HandleMessage (
360+ " delete" , R"( {"path": "/flags/flagA", "version": "not a number"})" );
361+
362+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
363+ }
364+
365+ TEST (DataSourceEventHandlerTests, HandlesPutWithInvalidFlagsType) {
366+ auto logger = launchdarkly::logging::NullLogger ();
367+ auto store = std::make_shared<MemoryStore>();
368+ DataSourceStatusManager manager;
369+ DataSourceEventHandler event_handler (*store, logger, manager);
370+
371+ // Flags should be an object, not a boolean
372+ auto res = event_handler.HandleMessage (
373+ " put" , R"( {"path": "/", "data": {"flags": true, "segments": {}}})" );
374+
375+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
376+ }
377+
378+ TEST (DataSourceEventHandlerTests, HandlesPutWithInvalidSegmentsType) {
379+ auto logger = launchdarkly::logging::NullLogger ();
380+ auto store = std::make_shared<MemoryStore>();
381+ DataSourceStatusManager manager;
382+ DataSourceEventHandler event_handler (*store, logger, manager);
383+
384+ // Segments should be an object, not an array
385+ auto res = event_handler.HandleMessage (
386+ " put" , R"( {"path": "/", "data": {"flags": {}, "segments": []}})" );
387+
388+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
389+ }
390+
391+ // Tests for additional malformed JSON variants
392+
393+ TEST (DataSourceEventHandlerTests, HandlesUnterminatedString) {
394+ auto logger = launchdarkly::logging::NullLogger ();
395+ auto store = std::make_shared<MemoryStore>();
396+ DataSourceStatusManager manager;
397+ DataSourceEventHandler event_handler (*store, logger, manager);
398+
399+ // Unterminated string should be treated as malformed JSON
400+ auto res = event_handler.HandleMessage (
401+ " patch" , R"( {"path": "/flags/x", "data": "unterminated)" );
402+
403+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
404+ }
405+
406+ TEST (DataSourceEventHandlerTests, HandlesTrailingComma) {
407+ auto logger = launchdarkly::logging::NullLogger ();
408+ auto store = std::make_shared<MemoryStore>();
409+ DataSourceStatusManager manager;
410+ DataSourceEventHandler event_handler (*store, logger, manager);
411+
412+ // Trailing comma should be treated as malformed JSON
413+ auto res = event_handler.HandleMessage (
414+ " patch" , R"( {"path": "/flags/x", "data": {},})" );
415+
416+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
417+ }
418+
419+ // Tests for missing required fields
420+
421+ TEST (DataSourceEventHandlerTests, HandlesDeleteWithMissingPath) {
422+ auto logger = launchdarkly::logging::NullLogger ();
423+ auto store = std::make_shared<MemoryStore>();
424+ DataSourceStatusManager manager;
425+ DataSourceEventHandler event_handler (*store, logger, manager);
426+
427+ // Initialize the store
428+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
429+
430+ // Missing path field should be treated as invalid
431+ auto res = event_handler.HandleMessage (
432+ " delete" , R"( {"version": 1})" );
433+
434+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
435+ }
436+
437+ TEST (DataSourceEventHandlerTests, HandlesDeleteWithMissingVersion) {
438+ auto logger = launchdarkly::logging::NullLogger ();
439+ auto store = std::make_shared<MemoryStore>();
440+ DataSourceStatusManager manager;
441+ DataSourceEventHandler event_handler (*store, logger, manager);
442+
443+ // Initialize the store
444+ event_handler.HandleMessage (" put" , R"( {"path":"/", "data":{}})" );
445+
446+ // Missing version field should be treated as invalid
447+ auto res = event_handler.HandleMessage (
448+ " delete" , R"( {"path": "/flags/flagA"})" );
449+
450+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kInvalidMessage , res);
451+ }
452+
453+ TEST (DataSourceEventHandlerTests, HandlesPutWithMissingPath) {
454+ auto logger = launchdarkly::logging::NullLogger ();
455+ auto store = std::make_shared<MemoryStore>();
456+ DataSourceStatusManager manager;
457+ DataSourceEventHandler event_handler (*store, logger, manager);
458+
459+ // Missing/empty path is treated as unrecognized (safely ignored)
460+ // This provides forward compatibility
461+ auto res = event_handler.HandleMessage (
462+ " put" , R"( {"data": {}})" );
463+
464+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kMessageHandled , res);
465+ }
466+
467+ TEST (DataSourceEventHandlerTests, HandlesEmptyJsonObject) {
468+ auto logger = launchdarkly::logging::NullLogger ();
469+ auto store = std::make_shared<MemoryStore>();
470+ DataSourceStatusManager manager;
471+ DataSourceEventHandler event_handler (*store, logger, manager);
472+
473+ // Empty JSON object with missing path is treated as unrecognized (safely ignored)
474+ // This provides forward compatibility with future event types
475+ auto res = event_handler.HandleMessage (" patch" , " {}" );
476+
477+ ASSERT_EQ (DataSourceEventHandler::MessageStatus::kMessageHandled , res);
478+ }
0 commit comments