GSaha567/seq_level_training_data
052
1text,length,is_long_context,metric_val,label_metric2"#include ""pch.h""3#include ""CppUnitTest.h""4#include ""DurationTracker.h""5#include ""PayloadEncoder.h""6#include ""EngageConstants.h""7#include ""MixpanelClient.h""8#include ""AsyncHelper.h""9 10using namespace std;11using namespace std::chrono;12using namespace Platform;13using namespace Platform::Collections;14using namespace Codevoid::Tests::Utilities;15using namespace Codevoid::Utilities::Mixpanel;16using namespace concurrency;17using namespace Microsoft::VisualStudio::CppUnitTestFramework;18using namespace Windows::Data::Json;19using namespace Windows::Foundation::Collections;20using namespace Windows::Storage;21using namespace Windows::Web::Http::Headers;22 23constexpr auto DEFAULT_TOKEN = L""DEFAULT_TOKEN"";24constexpr auto OVERRIDE_STORAGE_FOLDER = L""MixpanelClientTests"";25constexpr auto OVERRIDE_PROFILE_STORAGE_FOLDER = L""MixpanelClientTests\\\\Profile"";26constexpr milliseconds DEFAULT_IDLE_TIMEOUT = 10ms;27constexpr size_t SPIN_LOOP_LIMIT = 500;28constexpr auto DISTINCT_ENGAGE_KEY = L""$distinct_id"";29constexpr auto TOKEN_ENGAGE_KEY = L""$token"";30 31extern std::optional<steady_clock::time_point> g_overrideNextTimeAccess;32 33void SetNextClockAccessTime_MixpanelClient(const steady_clock::time_point& advanceTo)34{35 g_overrideNextTimeAccess = advanceTo;36}37 38void SpinWaitForItemCount(const atomic<int>& count, const int target)39{40 size_t loopCount = 0;41 while ((count.load() < target) && (loopCount < SPIN_LOOP_LIMIT))42 {43 loopCount++;44 this_thread::sleep_for(2ms);45 }46 47 wstring message = L""Spin Wait looped too long and never reached target. Actual Count: "";48 message += to_wstring(count);49 Assert::IsTrue(count.load() >= target, message.c_str());50}51 52IPropertySet^ GetPropertySetWithStuffInIt()53{54 auto properties = ref new PropertySet();55 properties->Insert(L""Key"", L""Value"");56 57 return properties;58}59 60namespace Codevoid::Tests::Mixpanel61{62 TEST_CLASS(MixpanelTests)63 {64 private:65 MixpanelClient^ m_client;66 67 static task<StorageFolder^> GetAndClearTestFolder(String^ folder)68 {69 auto storageFolder = co_await ApplicationData::Current->LocalFolder->CreateFolderAsync(70 folder,71 CreationCollisionOption::OpenIfExists);72 73 auto files = co_await storageFolder->GetFilesAsync();74 75 if (files->Size > 0)76 {77 for (auto&& fileToDelete : files)78 {79 co_await fileToDelete->DeleteAsync(StorageDeleteOption::PermanentDelete);80 }81 }82 83 return storageFolder;84 }85 86 static vector<IJsonValue^> CaptureRequestPayloads(IMap<String^, IJsonValue^>^ payload)87 {88 // Data is intended in the 'data' keyed item in the payload.89 // Assume it's a JsonArray...90 JsonArray^ data = dynamic_cast<JsonArray^>(payload->Lookup(L""data""));91 vector<IJsonValue^> items;92 93 // Copy into a vector for easier access.94 for (unsigned int i = 0; i < data->Size; i++)95 {96 items.push_back(data->GetAt(i));97 }98 99 return items;100 }101 102 static task<int> WriteTestPayload(String^ folderName)103 {104 auto storageFolder = co_await ApplicationData::Current->LocalFolder->CreateFolderAsync(folderName,105 CreationCollisionOption::OpenIfExists);106 107 JsonObject^ payload = ref new JsonObject();108 109 auto title = JsonValue::CreateStringValue(L""SampleTitle"");110 payload->Insert(L""title"", title);111 112 int itemsWritten = 1;113 for (; itemsWritten <= 3; itemsWritten++)114 {115 auto fileName = ref new String(std::to_wstring(itemsWritten).append(L"".json"").c_str());116 auto file = co_await storageFolder->CreateFileAsync(fileName, CreationCollisionOption::ReplaceExisting);117 co_await FileIO::WriteTextAsync(file, payload->Stringify());118 }119 120 return itemsWritten;121 }122 123 public:124 TEST_METHOD_INITIALIZE(InitializeClass)125 {126 m_client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));127 128 // Disable Persistence of Super properties129 // to help maintain these tests as stateless130 m_client->PersistSuperPropertiesToApplicationData = false;131 132 // We don't want the automatic session tracking133 // to add additional events when we're not expecting it.134 m_client->AutomaticallyTrackSessions = false;135 136 auto trackFolder = AsyncHelper::RunSynced(GetAndClearTestFolder(StringReference(OVERRIDE_STORAGE_FOLDER)));137 auto profileFolder = AsyncHelper::RunSynced(GetAndClearTestFolder(StringReference(OVERRIDE_PROFILE_STORAGE_FOLDER)));138 139 // The URL here is a helpful endpoint on the internet that just round-files140 // the requests to enable simple testing.141 m_client->Initialize(trackFolder, profileFolder, ref new Uri(L""https://jsonplaceholder.typicode.com/posts""));142 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 10);143 144 // Set the default service mock to avoid sending things to the145 // internet when we don't really need to.146 m_client->SetUploadToServiceMock([](auto uri, auto payload, auto)147 {148 return task_from_result(SendToServiceResult::SuccessfullySent);149 });150 }151 152 TEST_METHOD(InitializeAsyncRestoresQueuedToStorageItems)153 {154 // Since this test doesn't use the one created155 // in test init, lets shut it down156 m_client->Shutdown().wait();157 m_client = nullptr;158 159 AsyncHelper::RunSynced(WriteTestPayload(L""MixpanelUploadQueue""));160 AsyncHelper::RunSynced(WriteTestPayload(L""MixpanelUploadQueue\\\\Profile""));161 m_client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));162 m_client->DropEventsForPrivacy = false;163 m_client->PersistSuperPropertiesToApplicationData = false;164 165 AsyncHelper::RunSynced(m_client->InitializeAsync());166 167 168 atomic<int> itemCounts = 0;169 atomic<int> trackCount = 0;170 atomic<int> profileCount = 0;171 m_client->SetUploadToServiceMock([&itemCounts, &trackCount, &profileCount](Uri^ uri, auto payloads, auto)172 {173 auto convertedPayloads = MixpanelTests::CaptureRequestPayloads(payloads);174 if (uri->Path == StringReference(L""/track""))175 {176 trackCount += (int)convertedPayloads.size();177 }178 179 if (uri->Path == StringReference(L""/engage""))180 {181 profileCount += (int)convertedPayloads.size();182 }183 184 itemCounts += (int)convertedPayloads.size();185 return task_from_result(SendToServiceResult::SuccessfullySent);186 });187 188 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);189 m_client->Start();190 191 SpinWaitForItemCount(itemCounts, 6);192 193 Assert::AreEqual(6, itemCounts.load(), L""Persisted Items weren't supplied to upload correctly"");194 Assert::AreEqual(3, trackCount.load(), L""Wrong number of track payloads"");195 Assert::AreEqual(3, profileCount.load(), L""Wrong number of profile payloads"");196 197 AsyncHelper::RunSynced(m_client->ClearStorageAsync());198 m_client->Shutdown().wait();199 m_client = nullptr;200 }201 202#pragma region Tracking Events and Super Properties203 TEST_METHOD_CLEANUP(CleanupClass)204 {205 if (m_client == nullptr)206 {207 return;208 }209 210 AsyncHelper::RunSynced(m_client->Shutdown());211 }212 213 TEST_METHOD(TrackThrowsWithMissingEventName)214 {215 bool exceptionThrown = false;216 217 try218 {219 m_client->Track(nullptr, ref new ValueSet());220 }221 catch (InvalidArgumentException^ ex)222 {223 exceptionThrown = true;224 }225 226 Assert::IsTrue(exceptionThrown, L""Didn't get expected exception"");227 }228 229 TEST_METHOD(TrackThrowsIfNotInitialized)230 {231 bool exceptionThrown = false;232 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));233 234 try235 {236 client->Track(""Faux"", ref new ValueSet());237 }238 catch (InvalidArgumentException^ ex)239 {240 exceptionThrown = true;241 }242 243 Assert::IsTrue(exceptionThrown, L""Didn't get expected exception"");244 }245 246 TEST_METHOD(UpdateProfileThrowsIfNotInitialized)247 {248 bool exceptionThrown = false;249 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));250 251 try252 {253 client->UpdateProfile(UserProfileOperation::Set, ref new ValueSet());254 }255 catch (InvalidArgumentException^ ex)256 {257 exceptionThrown = true;258 }259 260 Assert::IsTrue(exceptionThrown, L""Didn't get expected exception"");261 }262 263 TEST_METHOD(UpdateProfileThrowsWhenNoPropertiesProvided)264 {265 bool exceptionThrown = false;266 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));267 268 try269 {270 client->UpdateProfile(UserProfileOperation::Set, nullptr);271 }272 catch (InvalidArgumentException^ ex)273 {274 exceptionThrown = true;275 }276 277 Assert::IsTrue(exceptionThrown, L""Didn't get expected exception"");278 }279 280 TEST_METHOD(UpdateProfileThrowsWhenEmptyPropertiesProvided)281 {282 bool exceptionThrown = false;283 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));284 285 try286 {287 client->UpdateProfile(UserProfileOperation::Set, ref new ValueSet());288 }289 catch (InvalidArgumentException^ ex)290 {291 exceptionThrown = true;292 }293 294 Assert::IsTrue(exceptionThrown, L""Didn't get expected exception"");295 }296 297 TEST_METHOD(GeneratingJsonObjectDoesntThrowForSupportedTypes)298 {299 IVector<String^>^ stringVector = ref new Vector<String^>();300 stringVector->Append(ref new String(L""1""));301 IVector<int>^ intVector = ref new Vector<int>({ 1, 2, 3 });302 IVector<float>^ floatVector = ref new Vector<float>({ 1.0f, 2.0f, 3.0f });303 IVector<double>^ doubleVector = ref new Vector<double>({ 1.0, 2.0, 3.0 });304 IPropertySet^ properties = ref new PropertySet();305 properties->Insert(L""StringValue"", L""Value"");306 properties->Insert(L""IntValue"", 42);307 properties->Insert(L""DoubleValue"", 4.0);308 properties->Insert(L""FloatValue"", 4.0f);309 properties->Insert(L""BooleanValue"", true);310 auto calendar = ref new Windows::Globalization::Calendar();311 properties->Insert(L""DateTimeValue"", calendar->GetDateTime());312 properties->Insert(L""StringVector"", stringVector);313 properties->Insert(L""IntegerVector"", intVector);314 //properties->Insert(L""FloatVector"", floatVector);315 //properties->Insert(L""DoubleVector"", doubleVector);316 317 JsonObject^ result = ref new JsonObject();318 try319 {320 MixpanelClient::AppendPropertySetToJsonPayload(properties, result);321 }322 catch (...)323 {324 // Thanks C++ unit testing, for not catching325 // WinRT exceptions326 Assert::Fail(L""Didn't Expect exception"");327 }328 }329 330 TEST_METHOD(GeneratingJsonObjectThrowsIfPropertySetIncludesUnsupportedType)331 {332 IPropertySet^ properties = ref new PropertySet();333 properties->Insert(L""StringValue"", L""Value"");334 properties->Insert(L""IntValue"", 42);335 properties->Insert(L""DoubleValue"", 4.0);336 properties->Insert(L""FloatValue"", 4.0f);337 properties->Insert(L""BooleanValue"", true);338 properties->Insert(L""BadValue"", ref new Windows::Foundation::Uri(L""http://foo.com""));339 340 JsonObject^ result = ref new JsonObject();341 bool exceptionThrown = false;342 try343 {344 MixpanelClient::AppendPropertySetToJsonPayload(properties, result);345 }346 catch (InvalidCastException^ ex)347 {348 exceptionThrown = true;349 }350 351 Assert::IsTrue(exceptionThrown, L""Didn't get exception for non-valuetype in property set"");352 }353 354 TEST_METHOD(CorrectJsonValuesAreGeneratedForSupportedTypes)355 {356 IVector<String^>^ stringVector = ref new Vector<String^>();357 stringVector->Append(ref new String(L""1""));358 IVector<int>^ intVector = ref new Vector<int>({ 1, 2, 3 });359 IVector<float>^ floatVector = ref new Vector<float>({ 1.0f, 2.0f, 3.0f });360 IVector<double>^ doubleVector = ref new Vector<double>({ 1.0, 2.0, 3.0 });361 362 IPropertySet^ properties = ref new PropertySet();363 properties->Insert(L""StringValue"", L""Value"");364 properties->Insert(L""IntValue"", 42);365 properties->Insert(L""DoubleValue"", 4.1);366 properties->Insert(L""FloatValue"", 4.2f);367 properties->Insert(L""BooleanValue"", true);368 auto calendar = ref new Windows::Globalization::Calendar();369 auto insertedDateTime = calendar->GetDateTime();370 properties->Insert(L""DateTimeValue"", insertedDateTime);371 properties->Insert(L""StringVector"", stringVector);372 properties->Insert(L""IntegerVector"", intVector);373 properties->Insert(L""FloatVector"", floatVector);374 properties->Insert(L""DoubleVector"", doubleVector);375 376 auto result = ref new JsonObject();377 MixpanelClient::AppendPropertySetToJsonPayload(properties, result);378 379 // Validate StringValue is present, and matches380 Assert::IsTrue(result->HasKey(L""StringValue""), L""StringValue key not present in JSON"");381 auto stringValue = result->GetNamedString(L""StringValue"");382 Assert::AreEqual(L""Value"", stringValue, L""Inserted string values didn't match"");383 384 // Validate that IntValue is present, and matches385 Assert::IsTrue(result->HasKey(L""IntValue""), L""IntValue not present"");386 int number = static_cast<int>(result->GetNamedNumber(L""IntValue""));387 Assert::AreEqual(42, number, L""IntValue doesn't match"");388 389 // Validate that that DoubleValue is present, and matches390 Assert::IsTrue(result->HasKey(L""DoubleValue""), L""DoubleValue not present"");391 double number2 = static_cast<double>(result->GetNamedNumber(L""DoubleValue""));392 Assert::AreEqual(4.1, number2, L""DoubleValue didn't match"");393 394 // Validate that FloatValue is present, and matches395 Assert::IsTrue(result->HasKey(L""FloatValue""), L""FloatValue key isn't present"");396 float number3 = static_cast<float>(result->GetNamedNumber(L""FloatValue""));397 Assert::AreEqual(4.2f, number3, L""FloatValue didn't match"");398 399 // Validate that BooleanValue is present and matches400 Assert::IsTrue(result->HasKey(L""BooleanValue""), L""BooleanValue key isn't present"");401 bool truthy = static_cast<bool>(result->GetNamedBoolean(L""BooleanValue""));402 Assert::AreEqual(true, truthy, L""BooleanValue didn't match"");403 404 // Validate that DateTimeValue is Present and matches405 Assert::IsTrue(result->HasKey(L""DateTimeValue""), L""DateTime key isn't present"");406 String^ dateTime = result->GetNamedString(L""DateTimeValue"");407 Assert::AreEqual(DateTimeToMixpanelDateFormat(insertedDateTime), dateTime, L""DateTimeValue didn't match"");408 409 Assert::IsTrue(result->HasKey(L""StringVector""), L""String Vector isn't present"");410 JsonArray^ stringArray = result->GetNamedArray(L""StringVector"");411 Assert::AreEqual(1, (int)stringArray->Size, L""Wrong Number of items in string vector"");412 Assert::AreEqual(L""1"", stringArray->GetStringAt(0), L""Wrong value in string vector"");413 414 Assert::IsTrue(result->HasKey(L""IntegerVector""), L""Integer Vector isn't present"");415 JsonArray^ integerArray = result->GetNamedArray(L""IntegerVector"");416 Assert::AreEqual(3, (int)integerArray->Size, L""Wrong Number of items in string vector"");417 Assert::AreEqual((double)1, integerArray->GetNumberAt(0), L""Wrong value in integer vector"");418 Assert::AreEqual((double)2, integerArray->GetNumberAt(1), L""Wrong value in integer vector"");419 Assert::AreEqual((double)3, integerArray->GetNumberAt(2), L""Wrong value in integer vector"");420 421 Assert::IsTrue(result->HasKey(L""FloatVector""), L""Float Vector isn't present"");422 JsonArray^ floatArray = result->GetNamedArray(L""FloatVector"");423 Assert::AreEqual(3, (int)floatArray->Size, L""Wrong Number of items in float vector"");424 Assert::AreEqual((double)1.0f, floatArray->GetNumberAt(0), L""Wrong value in float vector"");425 Assert::AreEqual((double)2.0f, floatArray->GetNumberAt(1), L""Wrong value in float vector"");426 Assert::AreEqual((double)3.0f, floatArray->GetNumberAt(2), L""Wrong value in float vector"");427 428 Assert::IsTrue(result->HasKey(L""DoubleVector""), L""Double Vector isn't present"");429 JsonArray^ doubleArray = result->GetNamedArray(L""DoubleVector"");430 Assert::AreEqual(3, (int)doubleArray->Size, L""Wrong Number of items in Double vector"");431 Assert::AreEqual((double)1.0f, doubleArray->GetNumberAt(0), L""Wrong value in Double vector"");432 Assert::AreEqual((double)2.0f, doubleArray->GetNumberAt(1), L""Wrong value in Double vector"");433 Assert::AreEqual((double)3.0f, doubleArray->GetNumberAt(2), L""Wrong value in Double vector"");434 }435 436 TEST_METHOD(ExceptionThrownWhenIncludingMpPrefixInPropertySet)437 {438 IPropertySet^ properties = ref new PropertySet();439 properties->Insert(L""mp_Foo"", L""Value"");440 441 bool exceptionThrown = false;442 JsonObject^ result = ref new JsonObject();443 444 try445 {446 MixpanelClient::AppendPropertySetToJsonPayload(properties, result);447 }448 catch (InvalidArgumentException^ ex)449 {450 exceptionThrown = true;451 }452 453 Assert::IsTrue(exceptionThrown, L""Didn't get exception for mp_ prefixed property in property set"");454 }455 456 TEST_METHOD(CanEncodeNumericValuesInJson)457 {458 IPropertySet^ properties = ref new PropertySet();459 properties->Insert(L""IntValue"", 42);460 properties->Insert(L""DoubleValue"", 4.1);461 properties->Insert(L""FloatValue"", 4.2f);462 463 auto result = ref new JsonObject();464 MixpanelClient::AppendNumericPropertySetToJsonPayload(properties, result);465 466 // Validate that IntValue is present, and matches467 Assert::IsTrue(result->HasKey(L""IntValue""), L""IntValue not present"");468 int number = static_cast<int>(result->GetNamedNumber(L""IntValue""));469 Assert::AreEqual(42, number, L""IntValue doesn't match"");470 471 // Validate that that DoubleValue is present, and matches472 Assert::IsTrue(result->HasKey(L""DoubleValue""), L""DoubleValue not present"");473 double number2 = static_cast<double>(result->GetNamedNumber(L""DoubleValue""));474 Assert::AreEqual(4.1, number2, L""DoubleValue didn't match"");475 476 // Validate that FloatValue is present, and matches477 Assert::IsTrue(result->HasKey(L""FloatValue""), L""FloatValue key isn't present"");478 float number3 = static_cast<float>(result->GetNamedNumber(L""FloatValue""));479 Assert::AreEqual(4.2f, number3, L""FloatValue didn't match"");480 }481 482 TEST_METHOD(ExceptionThrownWhenIncludingNonNumericValuesInPropertySet)483 {484 IPropertySet^ properties = ref new PropertySet();485 properties->Insert(L""Bar"", 3.14);486 properties->Insert(L""Foo"", L""Value"");487 488 bool exceptionThrown = false;489 JsonObject^ result = ref new JsonObject();490 491 try492 {493 MixpanelClient::AppendNumericPropertySetToJsonPayload(properties, result);494 }495 catch (InvalidCastException^ ex)496 {497 exceptionThrown = true;498 }499 500 Assert::IsTrue(exceptionThrown, L""Didn't get exception for non-numeric property in property set"");501 }502 503 TEST_METHOD(TrackingPayloadIncludesTokenAndPayload)504 {505 IPropertySet^ properties = ref new PropertySet();506 properties->Insert(L""StringValue"", L""Value"");507 508 properties = m_client->EmbelishPropertySetForTrack(properties);509 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);510 511 // Check that the event data is correct512 Assert::IsTrue(trackPayload->HasKey(L""event""), L""Didn't have event key"");513 Assert::AreEqual(L""TestEvent"", trackPayload->GetNamedString(""event""), L""Event name incorrect"");514 515 // Check that the actual properties we passed in are present516 Assert::IsTrue(trackPayload->HasKey(L""properties""), L""No properties payload"");517 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");518 519 // Validate StringValue is present, and matches520 Assert::IsTrue(propertiesPayload->HasKey(L""StringValue""), L""StringValue key not present in JSON"");521 auto stringValue = propertiesPayload->GetNamedString(L""StringValue"");522 Assert::AreEqual(L""Value"", stringValue, L""Inserted string values didn't match"");523 524 // Validate that the API Token is present525 Assert::IsTrue(propertiesPayload->HasKey(L""token""), L""No token in properties payload"");526 Assert::AreEqual(StringReference(DEFAULT_TOKEN), propertiesPayload->GetNamedString(L""token""), L""Token had incorrect value"");527 }528 529 TEST_METHOD(TrackingPayloadIncludesTokenAndSuperPropertiesPayload)530 {531 IPropertySet^ properties = ref new PropertySet();532 properties->Insert(L""StringValue"", L""Value"");533 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");534 m_client->SetSuperPropertyAsDouble(L""SuperPropertyB"", 7.0);535 m_client->SetSuperPropertyAsBoolean(L""SuperPropertyC"", true);536 m_client->SetSuperPropertyAsInteger(L""SuperPropertyD"", 1);537 538 properties = m_client->EmbelishPropertySetForTrack(properties);539 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);540 541 // Check that the event data is correct542 Assert::IsTrue(trackPayload->HasKey(L""event""), L""Didn't have event key"");543 Assert::AreEqual(L""TestEvent"", trackPayload->GetNamedString(""event""), L""Event name incorrect"");544 545 // Check that the actual properties we passed in are present546 Assert::IsTrue(trackPayload->HasKey(L""properties""), L""No properties payload"");547 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");548 549 // Validate StringValue is present, and matches550 Assert::IsTrue(propertiesPayload->HasKey(L""StringValue""), L""StringValue key not present in JSON"");551 auto stringValue = propertiesPayload->GetNamedString(L""StringValue"");552 Assert::AreEqual(L""Value"", stringValue, L""Inserted string values didn't match"");553 554 // Validate that the API Token is present555 Assert::IsTrue(propertiesPayload->HasKey(L""token""), L""No token in properties payload"");556 Assert::AreEqual(StringReference(DEFAULT_TOKEN), propertiesPayload->GetNamedString(L""token""), L""Token had incorrect value"");557 558 // Validate that Super Property A is present559 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyA""), L""No SuperPropertyA in properties payload"");560 Assert::AreEqual(L""SuperValueA"", propertiesPayload->GetNamedString(L""SuperPropertyA""), L""SuperPropertyA had incorrect value"");561 562 // Validate that Super Property B is present563 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyB""), L""No SuperPropertyB in properties payload"");564 Assert::AreEqual(7.0, propertiesPayload->GetNamedNumber(L""SuperPropertyB""), L""SuperPropertyB had incorrect value"");565 566 // Validate that Super Property C is present567 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyC""), L""No SuperPropertyC in properties payload"");568 Assert::AreEqual(true, propertiesPayload->GetNamedBoolean(L""SuperPropertyC""), L""SuperPropertyC had incorrect value"");569 570 // Validate that Super Property D is present571 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyD""), L""No SuperPropertyD in properties payload"");572 Assert::AreEqual(1.0, propertiesPayload->GetNamedNumber(L""SuperPropertyD""), L""SuperPropertyD had incorrect value"");573 }574 575 TEST_METHOD(CanSetSuperPropertyMoreThanOnce)576 {577 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");578 579 IPropertySet^ properties = m_client->EmbelishPropertySetForTrack(nullptr);580 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);581 582 // Check that the actual properties we passed in are present583 Assert::IsTrue(trackPayload->HasKey(L""properties""), L""No properties payload"");584 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");585 586 // Validate that Super Property is present587 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyA""), L""No SuperPropertyA in properties payload"");588 Assert::AreEqual(L""SuperValueA"", propertiesPayload->GetNamedString(L""SuperPropertyA""), L""SuperPropertyA had incorrect value"");589 590 // Set the super property a second time591 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""DifferentValue"");592 593 // Validate payload again594 properties = m_client->EmbelishPropertySetForTrack(nullptr);595 trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);596 propertiesPayload = trackPayload->GetNamedObject(""properties"");597 598 // Validate that Super Property is present599 Assert::IsTrue(propertiesPayload->HasKey(L""SuperPropertyA""), L""No SuperPropertyA in properties payload"");600 Assert::AreEqual(L""DifferentValue"", propertiesPayload->GetNamedString(L""SuperPropertyA""), L""SuperPropertyA had incorrect value"");601 }602 603 TEST_METHOD(CanCheckForSuperPropertyWhenNotSet)604 {605 Assert::IsFalse(m_client->HasSuperProperty(L""SuperPropertyA""), L""SuperPropertyA shouldn't have been in the list"");606 }607 608 TEST_METHOD(CanCheckForSuperPropertyWhenSet)609 {610 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");611 Assert::IsTrue(m_client->HasSuperProperty(L""SuperPropertyA""), L""SuperPropertyA not in list"");612 }613 614 TEST_METHOD(CanReadBackSuperProperties)615 {616 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");617 m_client->SetSuperPropertyAsBoolean(L""SuperPropertyB"", true);618 m_client->SetSuperPropertyAsDouble(L""SuperPropertyC"", 7.0);619 620 Assert::AreEqual(L""SuperValueA"", m_client->GetSuperPropertyAsString(L""SuperPropertyA""), L""SuperPropertyA didn't match"");621 Assert::IsTrue(m_client->GetSuperPropertyAsBool(L""SuperPropertyB""), L""SuperPropertyB didn't match"");622 Assert::AreEqual(7.0, m_client->GetSuperPropertyAsDouble(L""SuperPropertyC""), L""SuperPropertyC didn't match"");623 }624 625 TEST_METHOD(CanRemoveSuperProperty)626 {627 constexpr auto PROPERTY_NAME = L""SuperProperty"";628 629 // Add Property, and validate it actualy makes it before we630 // try to remove it631 m_client->SetSuperPropertyAsString(StringReference(PROPERTY_NAME), L""SuperValueA"");632 Assert::IsTrue(m_client->HasSuperProperty(StringReference(PROPERTY_NAME)), L""Property wasn't found; expected it"");633 634 m_client->RemoveSuperProperty(StringReference(PROPERTY_NAME));635 Assert::IsFalse(m_client->HasSuperProperty(StringReference(PROPERTY_NAME)), L""Proprety found; shouldn't have been present"");636 }637 638 TEST_METHOD(SuperPropertiesArePersistedAcrossClientInstances)639 {640 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));641 client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");642 643 client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));644 auto superPropertyValue = client->GetSuperPropertyAsString(L""SuperPropertyA"");645 Assert::AreEqual(L""SuperValueA"", superPropertyValue, ""Super Property wasn't persisted"");646 647 // Since we don't want to rely on the destruction of the648 // super properties in the clear method, lets just clear the local state649 AsyncHelper::RunSynced(ApplicationData::Current->ClearAsync());650 651 Assert::IsTrue(0 == ApplicationData::Current->LocalSettings->Containers->Size, L""Expected local data to be empty"");652 }653 654 TEST_METHOD(CanClearSuperProperties)655 {656 m_client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");657 658 // Validate that Super Property is present659 Assert::IsTrue(m_client->HasSuperProperty(L""SuperPropertyA""), L""No SuperPropertyA in properties payload"");660 Assert::AreEqual(L""SuperValueA"", m_client->GetSuperPropertyAsString(L""SuperPropertyA""), L""SuperPropertyA had incorrect value"");661 662 // Clear the super properties, and generate the payload again663 m_client->ClearSuperProperties();664 665 // Validate that Super Property isn't present666 Assert::IsFalse(m_client->HasSuperProperty(L""SuperPropertyA""), L""SuperPropertyA present, when it should have been cleared"");667 }668 669 TEST_METHOD(ClearingSuperPropertiesClearsAcrossInstances)670 {671 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));672 client->SetSuperPropertyAsString(L""SuperPropertyA"", L""SuperValueA"");673 674 client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));675 auto superPropertyValue = client->GetSuperPropertyAsString(L""SuperPropertyA"");676 Assert::AreEqual(L""SuperValueA"", superPropertyValue, ""Super Property wasn't persisted"");677 678 client->ClearSuperProperties();679 680 client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));681 Assert::IsFalse(client->HasSuperProperty(L""SuperPropertyA""), L""Didn't expect super property to be found"");682 }683 684 TEST_METHOD(SettingPropertyInPayloadOverridesSuperProperty)685 {686 constexpr auto SUPER_PROPERTY_VALUE = 7;687 constexpr auto LOCAL_PROPERTY_VALUE = 8;688 const auto propertyName = StringReference(L""SuperProperty"");689 m_client->SetSuperPropertyAsInteger(propertyName, SUPER_PROPERTY_VALUE);690 691 IPropertySet^ properties = ref new PropertySet();692 properties->Insert(propertyName, LOCAL_PROPERTY_VALUE);693 properties = m_client->EmbelishPropertySetForTrack(properties);694 695 auto retrievedValue = static_cast<int>(properties->Lookup(propertyName));696 Assert::AreEqual(LOCAL_PROPERTY_VALUE, retrievedValue, L""Property set didn't allow local value to override super properties"");697 }698 699 TEST_METHOD(SuperPropertiesAttachedOnNewInstanceWithoutSettingSuperProperty)700 {701 const auto PROPERTY_NAME = StringReference(L""SuperPropertyK"");702 const auto PROPERTY_VALUE = StringReference(L""PropertyValueK"");703 auto client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));704 client->SetSuperPropertyAsString(PROPERTY_NAME, PROPERTY_VALUE);705 706 client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));707 auto properties = client->EmbelishPropertySetForTrack(nullptr);708 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);709 710 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");711 Assert::IsTrue(propertiesPayload->HasKey(PROPERTY_NAME), L""Super Property not found"");712 Assert::AreEqual(PROPERTY_VALUE, propertiesPayload->GetNamedString(PROPERTY_NAME), L""Wrong value in the payload"");713 }714 715 TEST_METHOD(TimeOnlyAddedWhenAutomaticallyAttachingTimePropertyIsEnabled)716 {717 IPropertySet^ properties = ref new PropertySet();718 properties->Insert(L""StringValue"", L""Value"");719 m_client->AutomaticallyAttachTimeToEvents = false;720 721 properties = m_client->EmbelishPropertySetForTrack(properties);722 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);723 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");724 725 // Validate that the time property is not present (Since it was turned726 // off automatic attachment explicitly above)727 Assert::IsFalse(propertiesPayload->HasKey(L""time""), L""time key shouldn't be present"");728 729 // Turn the automatic attachment of time back on730 m_client->AutomaticallyAttachTimeToEvents = true;731 732 properties = m_client->EmbelishPropertySetForTrack(properties);733 trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);734 propertiesPayload = trackPayload->GetNamedObject(""properties"");735 736 // Validate that the time is present, and is non-zero737 Assert::IsTrue(propertiesPayload->HasKey(L""time""), L""No time in properties payload"");738 739 auto rawTimeValue = propertiesPayload->GetNamedValue(""time"");740 Assert::IsTrue(JsonValueType::Number == rawTimeValue->ValueType, L""Time was not the correct type"");741 Assert::AreNotEqual(0.0, rawTimeValue->GetNumber()); //, L""time shouldn't have been 0"");742 }743 744 TEST_METHOD(TimeDoesNotOverrideAnAlreadyExistingValueInThePropertiesPayload)745 {746 IPropertySet^ properties = ref new PropertySet();747 properties->Insert(L""StringValue"", L""Value"");748 properties->Insert(L""time"", L""fakevalue"");749 750 properties = m_client->EmbelishPropertySetForTrack(properties);751 auto trackPayload = MixpanelClient::GenerateTrackJsonPayload(L""TestEvent"", properties);752 auto propertiesPayload = trackPayload->GetNamedObject(""properties"");753 754 // Validate that the time is present, and is the same as our original value755 Assert::IsTrue(propertiesPayload->HasKey(L""time""), L""No time in properties payload"");756 757 auto rawTimeValue = propertiesPayload->GetNamedValue(""time"");758 Assert::IsFalse(JsonValueType::Number == rawTimeValue->ValueType, L""Time was not the correct type"");759 }760 761 TEST_METHOD(CanSetGetAndCheckSessionProperty)762 {763 m_client->SetSessionPropertyAsBoolean(L""SessionPropertyA"", true);764 m_client->SetSessionPropertyAsInteger(L""SessionPropertyB"", 1);765 m_client->SetSessionPropertyAsDouble(L""SessionPropertyC"", 1.0);766 m_client->SetSessionPropertyAsString(L""SessionPropertyD"", L""true"");767 768 Assert::IsFalse(m_client->HasSessionProperty(""SessionPropertyMissing""), L""Didn't expect to find non-existant property"");769 770 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyA""), L""SessionPropertyA not set"");771 Assert::AreEqual(true, m_client->GetSessionPropertyAsBool(L""SessionPropertyA""), L""SessionPropertyA had wrong value"");772 773 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyB""), L""SessionPropertyB not set"");774 Assert::AreEqual(1, m_client->GetSessionPropertyAsInteger(L""SessionPropertyB""), L""SessionPropertyB had wrong value"");775 776 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyC""), L""SessionPropertyC not set"");777 Assert::AreEqual(1.0, m_client->GetSessionPropertyAsDouble(L""SessionPropertyC""), L""SessionPropertyC had wrong value"");778 779 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyD""), L""SessionPropertyD not set"");780 Assert::AreEqual(L""true"", m_client->GetSessionPropertyAsString(L""SessionPropertyD""), L""SessionPropertyD had wrong value"");781 }782 783 TEST_METHOD(CanRemoveSessionProperty)784 {785 m_client->SetSessionPropertyAsBoolean(L""SessionPropertyA"", true);786 787 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyA""), L""SessionPropertyA not set"");788 789 m_client->RemoveSessionProperty(L""SessionPropertyA"");790 791 Assert::IsFalse(m_client->HasSessionProperty(L""SessionPropertyA""), L""SessionPropertyA should have been removed set"");792 }793 794 TEST_METHOD(CanClearSessionProperties)795 {796 m_client->SetSessionPropertyAsBoolean(L""SessionPropertyA"", true);797 m_client->SetSessionPropertyAsBoolean(L""SessionPropertyB"", true);798 799 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyA""), L""SessionPropertyA not set"");800 Assert::IsTrue(m_client->HasSessionProperty(L""SessionPropertyB""), L""SessionPropertyB not set"");801 802 m_client->ClearSessionProperties();803 804 Assert::IsFalse(m_client->HasSessionProperty(L""SessionPropertyA""), L""SessionPropertyA should have been removed set"");805 Assert::IsFalse(m_client->HasSessionProperty(L""SessionPropertyB""), L""SessionPropertyB should have been removed set"");806 }807#pragma endregion808 809#pragma region Asynchronous Queueing and Upload810 TEST_METHOD(QueuedEventsAreProcessedToStorage)811 {812 vector<shared_ptr<PayloadContainer>> trackWritten;813 vector<shared_ptr<PayloadContainer>> profileWritten;814 815 m_client->SetTrackWrittenToStorageMock([&trackWritten](auto wasWritten) {816 trackWritten.insert(begin(trackWritten), begin(wasWritten), end(wasWritten));817 });818 819 m_client->SetProfileWrittenToStorageMock([&profileWritten](auto wasWritten) {820 profileWritten.insert(begin(profileWritten), begin(wasWritten), end(wasWritten));821 });822 823 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);824 m_client->GenerateAndSetUserIdentity();825 826 m_client->Start();827 m_client->Track(L""TestEvent"", nullptr);828 m_client->UpdateProfile(UserProfileOperation::Set, GetPropertySetWithStuffInIt());829 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT);830 831 Assert::AreEqual(1, (int)trackWritten.size(), L""Event wasn't written to disk"");832 Assert::AreEqual(1, (int)profileWritten.size(), L""Profile update wasn't written to disk"");833 }834 835 TEST_METHOD(EventsAreNotProcessedWhenDropEventsForPrivacyIsEnabled)836 {837 m_client->DropEventsForPrivacy = true;838 839 vector<shared_ptr<PayloadContainer>> written;840 841 m_client->SetTrackWrittenToStorageMock([&written](auto wasWritten) {842 written.insert(begin(written), begin(wasWritten), end(wasWritten));843 });844 845 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);846 847 m_client->Start();848 m_client->Track(L""TestEvent"", nullptr);849 850 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT);851 AsyncHelper::RunSynced(m_client->Shutdown());852 853 Assert::AreEqual(0, (int)written.size(), L""Event event shouldn't have been written to disk"");854 }855 856 TEST_METHOD(ProfileUpdatesAreDroppedWhenPrivacyIsEnabled)857 {858 m_client->DropEventsForPrivacy = true;859 860 vector<shared_ptr<PayloadContainer>> written;861 862 m_client->SetTrackWrittenToStorageMock([&written](auto wasWritten) {863 written.insert(begin(written), begin(wasWritten), end(wasWritten));864 });865 866 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);867 868 auto properties = ref new PropertySet();869 properties->Insert(L""Property"", L""Value"");870 871 m_client->Start();872 m_client->UpdateProfile(UserProfileOperation::Set, properties);873 874 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT);875 AsyncHelper::RunSynced(m_client->Shutdown());876 877 Assert::AreEqual(0, (int)written.size(), L""Event event shouldn't have been written to disk"");878 }879 880 TEST_METHOD(EventsAlreadyInStorageAreIgnoredWhenDroppingForPrivacyEnabledBeforeStartup)881 {882 // Since this test doesn't use the one created883 // in test init, lets shut it down884 m_client->Shutdown().wait();885 m_client = nullptr;886 887 AsyncHelper::RunSynced(WriteTestPayload(L""MixpanelUploadQueue""));888 AsyncHelper::RunSynced(WriteTestPayload(L""MixpanelUploadQueue\\\\Profile""));889 m_client = ref new MixpanelClient(StringReference(DEFAULT_TOKEN));890 m_client->DropEventsForPrivacy = true;891 m_client->PersistSuperPropertiesToApplicationData = false;892 893 AsyncHelper::RunSynced(m_client->InitializeAsync());894 895 atomic<int> itemCounts = 0;896 m_client->SetUploadToServiceMock([&itemCounts](Uri^ uri, auto payloads, auto)897 {898 itemCounts += 1;899 return task_from_result(SendToServiceResult::SuccessfullySent);900 });901 902 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);903 m_client->Start();904 905 AsyncHelper::RunSynced(m_client->ClearStorageAsync());906 m_client->Shutdown().wait();907 m_client = nullptr;908 909 Assert::AreEqual(0, itemCounts.load(), L""Items Shouldn't have been uploaded"");910 }911 912 TEST_METHOD(QueueCanBePaused)913 {914 m_client->Start();915 916 m_client->Track(L""TestEvent"", nullptr);917 AsyncHelper::RunSynced(m_client->PauseAsync());918 }919 920 TEST_METHOD(QueueCanBeCleared)921 {922 m_client->GenerateAndSetUserIdentity();923 m_client->ForceWritingToStorage();924 m_client->Start();925 m_client->Track(L""TestEvent"", nullptr);926 m_client->UpdateProfile(UserProfileOperation::Set, GetPropertySetWithStuffInIt());927 928 AsyncHelper::RunSynced(m_client->PauseAsync());929 930 auto trackFileCount = AsyncHelper::RunSynced(create_task([]() -> task<int> {931 auto folder = co_await ApplicationData::Current->LocalFolder->GetFolderAsync(StringReference(OVERRIDE_STORAGE_FOLDER));932 auto files = co_await folder->GetFilesAsync();933 934 return files->Size;935 }));936 937 auto profileFileCount = AsyncHelper::RunSynced(create_task([]() -> task<int> {938 auto folder = co_await ApplicationData::Current->LocalFolder->GetFolderAsync(StringReference(OVERRIDE_PROFILE_STORAGE_FOLDER));939 auto files = co_await folder->GetFilesAsync();940 941 return files->Size;942 }));943 944 Assert::AreEqual(1, trackFileCount, L""Wrong number of track persisted items found"");945 Assert::AreEqual(1, profileFileCount, L""Wrong number of profile persisted items found"");946 947 AsyncHelper::RunSynced(m_client->ClearStorageAsync());948 949 trackFileCount = AsyncHelper::RunSynced(create_task([]() -> task<int> {950 auto folder = co_await ApplicationData::Current->LocalFolder->GetFolderAsync(StringReference(OVERRIDE_STORAGE_FOLDER));951 auto files = co_await folder->GetFilesAsync();952 953 return files->Size;954 }));955 956 Assert::AreEqual(0, trackFileCount, L""Didn't expect to find any items"");957 958 profileFileCount = AsyncHelper::RunSynced(create_task([]() -> task<int> {959 auto folder = co_await ApplicationData::Current->LocalFolder->GetFolderAsync(StringReference(OVERRIDE_PROFILE_STORAGE_FOLDER));960 auto files = co_await folder->GetFilesAsync();961 962 return files->Size;963 }));964 965 Assert::AreEqual(0, profileFileCount, L""Didn't expect to find any items"");966 }967 968 TEST_METHOD(RequestIndicatesFailureWhenCallingNonExistantEndPoint)969 {970 auto payload = ref new Map<String^, IJsonValue^>();971 auto wasSuccessful = MixpanelClient::SendRequestToService(972 ref new Uri(L""https://fake.codevoid.net""),973 payload,974 ref new HttpProductInfoHeaderValue(L""Codevoid.Mixpanel.MixpanelTests"", L""1.0"")).get();975 Assert::IsFalse(SendToServiceResult::SuccessfullySent == wasSuccessful, L""Was not supposed to be successful"");976 }977 978 TEST_METHOD(CanMakeRequestToPlaceholderService)979 {980 auto payload = ref new Map<String^, IJsonValue^>();981 payload->Insert(L""data"", JsonObject::Parse(""{ \\""data\\"": 0 }""));982 auto wasSuccessful = MixpanelClient::SendRequestToService(983 ref new Uri(L""https://jsonplaceholder.typicode.com/posts""),984 payload,985 ref new HttpProductInfoHeaderValue(L""Codevoid.Mixpanel.MixpanelTests"", L""1.0"")).get();986 Assert::IsTrue(SendToServiceResult::SuccessfullySent == wasSuccessful, L""Result was not a success"");987 }988 989 TEST_METHOD(QueueIsUploaded)990 {991 vector<vector<IJsonValue^>> trackPayloads;992 vector<vector<IJsonValue^>> profilePayloads;993 m_client->SetUploadToServiceMock([&trackPayloads, &profilePayloads](Uri^ uri, auto payloads, auto)994 {995 auto capturedPayload = MixpanelTests::CaptureRequestPayloads(payloads);996 if (uri->Path == StringReference(L""/track""))997 {998 trackPayloads.push_back(capturedPayload);999 }1000 1001 if (uri->Path == StringReference(L""/engage""))1002 {1003 profilePayloads.push_back(capturedPayload);1004 }1005 1006 return task_from_result(SendToServiceResult::SuccessfullySent);1007 });1008 1009 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);1010 m_client->GenerateAndSetUserIdentity();1011 m_client->Start();1012 m_client->Track(L""TestEvent"", nullptr);1013 m_client->UpdateProfile(UserProfileOperation::Set, GetPropertySetWithStuffInIt());1014 1015 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT * 100);1016 1017 Assert::AreEqual(1, (int)trackPayloads.size(), L""Wrong number of track payloads sent"");1018 Assert::AreEqual(1, (int)(trackPayloads[0].size()), L""Wrong number of items in the first track payload"");1019 Assert::AreEqual(1, (int)profilePayloads.size(), L""Wrong number of profile payloads sent"");1020 Assert::AreEqual(1, (int)(profilePayloads[0].size()), L""Wrong number of items in the first profile payload"");1021 }1022 1023 TEST_METHOD(BatchesIncludeMoreThanOneItem)1024 {1025 vector<vector<IJsonValue^>> capturedPayloads;1026 m_client->SetUploadToServiceMock([&capturedPayloads](auto, auto payloads, auto)1027 {1028 capturedPayloads.push_back(MixpanelTests::CaptureRequestPayloads(payloads));1029 return task_from_result(SendToServiceResult::SuccessfullySent);1030 });1031 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 4);1032 1033 m_client->Track(L""TestEvent1"", nullptr);1034 m_client->Track(L""TestEvent2"", nullptr);1035 m_client->Track(L""TestEvent3"", nullptr);1036 m_client->Track(L""TestEvent4"", nullptr);1037 1038 m_client->Start();1039 1040 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT);1041 1042 Assert::AreEqual(1, (int)capturedPayloads.size(), L""Wrong number of payloads sent"");1043 Assert::AreEqual(4, (int)(capturedPayloads[0].size()), L""Wrong number of items in the first payload"");1044 }1045 1046 TEST_METHOD(ItemsAreSpreadAcrossMultipleBatches)1047 {1048 vector<vector<IJsonValue^>> capturedPayloads;1049 m_client->SetUploadToServiceMock([&capturedPayloads](auto, auto payloads, auto)1050 {1051 capturedPayloads.push_back(MixpanelTests::CaptureRequestPayloads(payloads));1052 return task_from_result(SendToServiceResult::SuccessfullySent);1053 });1054 1055 for (int i = 0; i < 150; i++)1056 {1057 m_client->Track(L""TrackEvent"", nullptr);1058 }1059 1060 m_client->Start();1061 1062 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT * 10);1063 1064 Assert::AreEqual(3, (int)capturedPayloads.size(), L""Wrong number of payloads sent"");1065 Assert::AreEqual(50, (int)(capturedPayloads[0].size()), L""Wrong number of items in the first payload"");1066 Assert::AreEqual(50, (int)(capturedPayloads[1].size()), L""Wrong number of items in the second payload"");1067 Assert::AreEqual(50, (int)(capturedPayloads[2].size()), L""Wrong number of items in the third payload"");1068 }1069 1070 TEST_METHOD(ItemsAreRetriedIndividuallyAfterAFailure)1071 {1072 shared_ptr<vector<int>> capturedPayloadCounts = make_shared<vector<int>>();1073 int itemsSeenCount = 0;1074 atomic<int> itemsSuccessfullyUploaded = 0;1075 constexpr int FAILURE_TRIGGER = 75; // Fail in second batch1076 1077 m_client->SetUploadToServiceMock([capturedPayloadCounts, &itemsSeenCount, &itemsSuccessfullyUploaded, &FAILURE_TRIGGER](auto, auto payloads, auto)1078 {1079 int itemsInThisBatch = 0;1080 1081 for (auto item : MixpanelTests::CaptureRequestPayloads(payloads))1082 {1083 itemsSeenCount++;1084 itemsInThisBatch++;1085 if (itemsSeenCount == FAILURE_TRIGGER)1086 {1087 return task_from_result(SendToServiceResult::FailedAtService);1088 }1089 }1090 1091 capturedPayloadCounts->push_back(itemsInThisBatch);1092 itemsSuccessfullyUploaded += itemsInThisBatch;1093 1094 return task_from_result(SendToServiceResult::SuccessfullySent);1095 });1096 1097 for (int i = 0; i < 100; i++)1098 {1099 m_client->Track(L""TrackEvent"", nullptr);1100 }1101 1102 m_client->Start();1103 1104 SpinWaitForItemCount(itemsSuccessfullyUploaded, 100);1105 1106 Assert::AreEqual(51, (int)capturedPayloadCounts->size(), L""Wrong number of payloads sent"");1107 for (int i = 1; i < 50; i++)1108 {1109 Assert::AreEqual(1, (*capturedPayloadCounts)[i], L""Wrong number of items in single-item-payloads"");1110 }1111 1112 for (int i = 0; i < 50; i++)1113 {1114 m_client->Track(L""TrackEvent"", nullptr);1115 }1116 1117 SpinWaitForItemCount(itemsSuccessfullyUploaded, 150);1118 1119 Assert::AreEqual(52, (int)capturedPayloadCounts->size(), L""Wrong number of payloads sent"");1120 Assert::AreEqual(50, (*capturedPayloadCounts)[51], L""Wrong number of items in the third payload"");1121 1122 m_client->Shutdown().wait();1123 m_client = nullptr;1124 }1125 1126 TEST_METHOD(ItemsThatFailAreRetriedMoreThanOnce)1127 {1128 int event1Count = 0;1129 int event2Count = 0;1130 int event3Count = 0;1131 atomic<int> itemCount = 0;1132 1133 m_client->SetUploadToServiceMock([&event1Count, &event2Count, &event3Count, &itemCount](auto, auto payloads, auto)1134 {1135 SendToServiceResult successful = SendToServiceResult::SuccessfullySent;1136 1137 for (auto item : MixpanelTests::CaptureRequestPayloads(payloads))1138 {1139 auto asObject = static_cast<JsonObject^>(item);1140 auto eventName = asObject->GetNamedString(L""event"");1141 if (eventName == L""TrackEvent1"")1142 {1143 event1Count++;1144 }1145 else if (eventName == L""TrackEvent2"")1146 {1147 event2Count++;1148 // Fail the batch the first two times1149 if (event2Count < 3)1150 {1151 successful = SendToServiceResult::FailedAtService;1152 }1153 }1154 else if (eventName == L""TrackEvent3"")1155 {1156 event3Count++;1157 }1158 1159 itemCount++;1160 }1161 1162 return task_from_result(successful);1163 });1164 1165 m_client->Track(L""TrackEvent1"", nullptr);1166 m_client->Track(L""TrackEvent2"", nullptr);1167 m_client->Track(L""TrackEvent3"", nullptr);1168 1169 m_client->Start();1170 1171 SpinWaitForItemCount(itemCount, 7);1172 1173 Assert::AreEqual(2, event1Count, L""Should only see event 1 twice - once in first payload, second in individual payload"");1174 Assert::AreEqual(2, event3Count, L""Should only see event 3 once - once in first payload, second in individual payload"");1175 Assert::AreEqual(3, event2Count, L""Event 2 should have been retried twice, and once successfully"");1176 1177 m_client->Shutdown().wait();1178 m_client = nullptr;1179 }1180 1181 TEST_METHOD(DurationIsAutomaticallyAttached)1182 {1183 vector<vector<IJsonValue^>> capturedPayloads;1184 m_client->SetUploadToServiceMock([&capturedPayloads](auto, auto payloads, auto)1185 {1186 capturedPayloads.push_back(MixpanelTests::CaptureRequestPayloads(payloads));1187 return task_from_result(SendToServiceResult::SuccessfullySent);1188 });1189 1190 m_client->ConfigureForTesting(DEFAULT_IDLE_TIMEOUT, 1);1191 1192 m_client->Start();1193 auto now = chrono::steady_clock::now();1194 SetNextClockAccessTime_MixpanelClient(now);1195 m_client->StartTimedEvent(L""TestEvent"");1196 1197 SetNextClockAccessTime_MixpanelClient(now + 1000ms);1198 m_client->Track(L""TestEvent"", nullptr);1199 1200 this_thread::sleep_for(DEFAULT_IDLE_TIMEOUT);