@@ -235,4 +235,57 @@ TEST(AIJsonSerializerTests, replacesInvalidUtf8BytesInStrings)
235235 EXPECT_EQ (std::string::npos, actual.find (static_cast <char >(0xA9 )));
236236}
237237
238- #endif // HAVE_MAT_AI
238+ TEST (AIJsonSerializerTests, replacesInvalidUtf8BytesInTags)
239+ {
240+ std::unique_ptr<AIJsonSerializer> aiSerializer = std::make_unique<AIJsonSerializer>();
241+
242+ // Inject an invalid UTF-8 byte (0xA9) into a tag value.
243+ // With json::error_handler_t::replace, serialization should not throw and
244+ // the invalid byte should be replaced by the UTF-8 encoding of U+FFFD.
245+ std::string invalidUtf8 = " app" ;
246+ invalidUtf8.push_back (static_cast <char >(0xA9 ));
247+ invalidUtf8 += " Ver" ;
248+
249+ ::CsProtocol::App app;
250+ app.ver = invalidUtf8;
251+ app.locale = " appLocale" ;
252+
253+ ::CsProtocol::Device device;
254+ device.localId = " deviceId" ;
255+ device.deviceClass = " deviceClass" ;
256+
257+ ::CsProtocol::Protocol proto;
258+ proto.devMake = " protoDevMake" ;
259+ proto.devModel = " protoDevModel" ;
260+
261+ ::CsProtocol::Os os;
262+ os.name = " osName" ;
263+ os.ver = " osVer" ;
264+
265+ ::CsProtocol::User user;
266+ user.localId = " userId" ;
267+
268+ ::CsProtocol::Data data;
269+ ::CsProtocol::Value prop;
270+ prop.stringValue = " ok" ;
271+ data.properties [" prop" ] = prop;
272+
273+ std::unique_ptr<::CsProtocol::Record> record = createTestRecord (
274+ " eventUtf8Tags" , 1 , app, device, proto, os, user, data
275+ );
276+ IncomingEventContext context (PAL::generateUuidString (), TEST_TOKEN, EventLatency_Unspecified, EventPersistence_Normal, record.get ());
277+
278+ EXPECT_NO_THROW (aiSerializer->serialize (&context));
279+ ASSERT_FALSE (context.record .blob .empty ());
280+
281+ ::nlohmann::json parsed;
282+ EXPECT_NO_THROW (parsed = nlohmann::json::parse (context.record .blob .begin (), context.record .blob .end ()));
283+
284+ const std::string actual = parsed[" tags" ][" ai.application.ver" ].get <std::string>();
285+ const std::string expected = std::string (" app" ) + " \xEF\xBF\xBD " + " Ver" ;
286+
287+ EXPECT_EQ (expected, actual);
288+ EXPECT_EQ (std::string::npos, actual.find (static_cast <char >(0xA9 )));
289+ }
290+
291+ #endif // HAVE_MAT_AI
0 commit comments