Skip to content

Commit

Permalink
Integration tests, logger levels, magicswap tweaks, chain switching i…
Browse files Browse the repository at this point in the history
…mprovements (#86)

* Add integration test for connect flow

* Add loggerLevel and autoInitialize properties to TDKConfig

* Fix Magicswap type (priceUSD). Improve error handling on UI

* Refactor TDKLogger

* Use LogDebug to log event payload. Allow config of different log level for prod

* Tweak MagicswapUI responsiveness

* Use debug logger in tests. Update integration tests

* Skip unnecessarily re-initializing thirdweb sdk when switching to same chain

* Tests for IdentityUI

* Properly reconnect when switching chains after socials login

* Refactor reconnect methods. Expose Reconnect(email) method

* Rename methods for clarity
  • Loading branch information
lefarchi authored Jul 26, 2024
1 parent 560fb2b commit c990482
Show file tree
Hide file tree
Showing 21 changed files with 584 additions and 170 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
using System.Threading;
using System.Text;
using System;
using System.Linq;

// For running these tests you need to modify scripting defines: remove TDK_HELIKA and add TREASURE_ANALYTICS
// It needs to be done manually for now, we might want a better way, perhaps something like this:
// https://forum.unity.com/threads/info-on-unity_include_tests-define.890095/#post-7495937
// to remove/add scripting defines programatically, perhaps a custom editor button can do that + run the tests

Expand Down Expand Up @@ -103,9 +100,13 @@ void ClearPersistedEventBatches()
}

void MockAnalyticsRequest(HttpStatusCode statusCode, string jsonResponse) {
#if !UNITY_WEBGL
TDKServiceLocator.GetService<TDKAnalyticsService>().SetHttpMessageHandler(
new MockHttpMessageHandler(statusCode, jsonResponse)
);
#else
throw new Exception("Mocking requests for webgl not implemented");
#endif
}

System.Collections.Generic.List<string> logs = new();
Expand Down Expand Up @@ -168,6 +169,9 @@ public IEnumerator MySetUp()
prodAnalyticsApiUrl = "https://localhost:5000/prodAnalyticsApiUrl",
sessionLengthDays = 123
});
var thirdwebConfig = ScriptableObject.CreateInstance<TDKThirdwebConfig>();
testTDKConfig.SetModuleConfig(thirdwebConfig);

testTDKAbstractedEngineApi = new TestTDKAbstractedEngineApi();

ClearPersistedEventBatches();
Expand All @@ -184,12 +188,17 @@ public void MyTearDown()
[UnityTest]
public IEnumerator AnalyticsTestComplex1()
{
TDK.Instance.InitializeProperties(
TDK.Initialize(
testTDKConfig,
testTDKAbstractedEngineApi,
new LocalSettings(testTDKAbstractedEngineApi.ApplicationPersistentDataPath())
);
TDK.Instance.InitializeSubsystems();

yield return TestHelpers.WaitUntilWithMax(() => logs.Count >= 2, 5);

string expectedPersistancePath = Path.Combine(testTDKAbstractedEngineApi.ApplicationPersistentDataPath(), AnalyticsConstants.PERSISTENT_DIRECTORY_NAME);
ValidateNextLog($"[TDKAnalyticsService.Cache:InitPersistentCache] _persistentFolderPath: {expectedPersistancePath}");
ValidateNextLog("rgx:.* Got server epoch time: \\d+");

yield return TestHighPrioEvent();
yield return TestBatchEvents();
Expand All @@ -202,10 +211,8 @@ IEnumerator TestHighPrioEvent() {
TDK.Analytics.TrackCustomEvent(testEventName, null, highPriority: true);
yield return new WaitForSeconds(2);

string expectedPersistancePath = Path.Combine(testTDKAbstractedEngineApi.ApplicationPersistentDataPath(), AnalyticsConstants.PERSISTENT_DIRECTORY_NAME);
string expectedRoute = "https://localhost:5000/devAnalyticsApiUrl/events";
string expectedPayload = $".*\"name\":\"{testEventName}\".*";
ValidateNextLog($"[TDKAnalyticsService.Cache:InitPersistentCache] _persistentFolderPath: {expectedPersistancePath}");
ValidateNextLog(
$"rgx:{Regex.Escape("Intercepted request to the following route: " + expectedRoute)} - payload: {expectedPayload}"
);
Expand Down
File renamed without changes.
217 changes: 217 additions & 0 deletions Assets/TestsBasic/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Treasure;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
using UnityEngine.UI;

public class IntegrationTests
{
[UnitySetUp]
public IEnumerator MySetUp()
{
var testTDKAbstractedEngineApi = TestHelpers.GetTestAbstractedEngineApi();
var tdkConfig = TDKConfig.LoadFromResources();
Assert.That(tdkConfig.AutoInitialize, Is.False, "TDKConfig AutoInitialize must be false when testing");

tdkConfig.Environment = TDKConfig.Env.DEV;
tdkConfig.LoggerLevel = TDKConfig.LoggerLevelValue.DEBUG;
TDK.Initialize(
tdkConfig: tdkConfig,
testTDKAbstractedEngineApi,
new LocalSettings(testTDKAbstractedEngineApi.ApplicationPersistentDataPath())
);

TestHelpers.ClearPersistedEventBatches(testTDKAbstractedEngineApi);

yield return null;
}

List<string> tdkLogs;
bool connected = false;

[UnityTest]
public IEnumerator Integration1()
{
tdkLogs = new List<string>();
TDKLogger.ExternalLogCallback += (msg) => { tdkLogs.Add(msg); };

SceneManager.LoadScene("Assets/Treasure/Example/Scenes/TDKHarness.unity", LoadSceneMode.Single);

yield return new WaitForSeconds(1);

yield return ConnectViaUI();

yield return new WaitForSeconds(1);

if (connected) {
yield return SetChain();
}

yield return SendAnalyticsEvents();

yield return new WaitForSeconds(1);

yield return CreateSession();

yield return new WaitForSeconds(1);

yield return ForceFlushCache();
}

// Note: this does not work on webgl by default, for socials login (oauth) we need a host with cors enabled
private IEnumerator ConnectViaUI()
{
var navButtonConnect = GameObject.Find("Connect_Btn");
navButtonConnect.GetComponent<Button>().onClick.Invoke();

var connectButton = GameObject.Find("Btn_ConnectWallet");
var loginModal = Object.FindAnyObjectByType<LoginModal>(FindObjectsInactive.Include);

Assert.That(connectButton.activeInHierarchy, Is.True);
Assert.That(loginModal.gameObject.activeInHierarchy, Is.False);
connectButton.GetComponent<Button>().onClick.Invoke();
yield return null;
Assert.That(loginModal.gameObject.activeInHierarchy, Is.True);

var accountModal = Object.FindAnyObjectByType<AccountModal>(FindObjectsInactive.Include);
var headerLogo = Object.FindAnyObjectByType<HeaderLogo>();
var googleButton = GameObject.Find("ButtonFrameIcon(Google)");
var backgroundButton = GameObject.Find("TransparentOverlayButton");

Assert.That(headerLogo.GetCurrentNameText(), Is.EqualTo("Loading..."));
googleButton.GetComponent<Button>().onClick.Invoke();
yield return TestHelpers.WaitUntilWithMax(() => headerLogo.GetCurrentNameText() != "Loading...", 10f);
Assert.That(headerLogo.GetCurrentNameText(), Is.EqualTo("TDK Harness"));
yield return TestHelpers.WaitUntilWithMax(() => TDK.Connect.Address != null, 30f);
Assert.That(loginModal.gameObject.activeInHierarchy, Is.False);
Assert.That(accountModal.gameObject.activeInHierarchy, Is.False);

yield return new WaitForSeconds(1);

connectButton.GetComponent<Button>().onClick.Invoke();
Assert.That(loginModal.gameObject.activeInHierarchy, Is.False);
Assert.That(accountModal.gameObject.activeInHierarchy, Is.True);
Assert.That(accountModal.GetAddressText(), Does.StartWith(TDK.Connect.Address[..6]));
yield return new WaitForSeconds(3);
backgroundButton.GetComponent<Button>().onClick.Invoke();
Assert.That(accountModal.gameObject.activeInHierarchy, Is.False);

connected = true;
}

private IEnumerator SetChain()
{
yield return ForceFlushCache();

tdkLogs.Clear();

yield return TestHelpers.WaitForTask(TDK.Connect.SetChainId(ChainId.ArbitrumSepolia));
yield return TestHelpers.WaitForTask(TDK.Connect.SetChainId(ChainId.Arbitrum));
yield return TestHelpers.WaitForTask(TDK.Connect.SetChainId(ChainId.Arbitrum));
yield return TestHelpers.WaitForTask(TDK.Connect.SetChainId(ChainId.ArbitrumSepolia));

Assert.That(tdkLogs.Count, Is.EqualTo(10));

Assert.That(tdkLogs, Is.EqualTo(new List<string> {
"Chain is already set to ArbitrumSepolia",
"Initializing Thirdweb SDK for chain: arbitrum",
"[TDK.Connect:Connect] Connecting to SmartWallet...",
"[TDK.Connect:Connect] Connection success!",
"Switched chain to Arbitrum",
"Chain is already set to Arbitrum",
"Initializing Thirdweb SDK for chain: arbitrum-sepolia",
"[TDK.Connect:Connect] Connecting to SmartWallet...",
"[TDK.Connect:Connect] Connection success!",
"Switched chain to ArbitrumSepolia"
}));
}

private IEnumerator SendAnalyticsEvents()
{
var navButtonAnalytics = GameObject.Find("Analytics_Btn");
navButtonAnalytics.GetComponent<Button>().onClick.Invoke();

var trackCustomEventButton = GameObject.Find("Btn_TrackCustomEvent");

Assert.That(trackCustomEventButton.activeInHierarchy, Is.True);

yield return ForceFlushCache();

tdkLogs.Clear();
trackCustomEventButton.GetComponent<Button>().onClick.Invoke();

yield return TestHelpers.WaitUntilWithMax(() => tdkLogs.Count >= 2, 15f);

Assert.That(tdkLogs.Count, Is.EqualTo(2));

var eventsArray = AnalyticsEvent.ExtractPayloadFromLog(tdkLogs[0]);
Assert.That(eventsArray.Count, Is.EqualTo(1));
Assert.That(eventsArray[0].name, Is.EqualTo("custom_event"));
Assert.That(tdkLogs[1], Is.EqualTo("[TDKAnalyticsService.IO:SendEvents] Events sent successfully"));
}

private IEnumerator CreateSession()
{
yield return ForceFlushCache();

tdkLogs.Clear();

Assert.That(TDK.Identity.IsAuthenticated, Is.False);
yield return TestHelpers.WaitForTask(TDK.Identity.StartUserSession(), 10);
Assert.That(TDK.Identity.IsAuthenticated, Is.True);

Assert.That(tdkLogs.Count, Is.EqualTo(5));
Assert.That(tdkLogs[0], Is.EqualTo("Fetching login payload"));
Assert.That(tdkLogs[1], Is.EqualTo("Signing login payload"));
Assert.That(tdkLogs[2], Is.EqualTo("Logging in and fetching TDK auth token"));
Assert.That(tdkLogs[3], Is.EqualTo("Using existing session key").Or.EqualTo("Creating new session key"));
Assert.That(tdkLogs[4], Is.EqualTo("User session started successfully"));

tdkLogs.Clear();

yield return TestHelpers.WaitForTask(TDK.Identity.StartUserSession(), 10);

Assert.That(tdkLogs, Is.EqualTo(new List<string> {
"Validating existing user session",
"Fetching user details",
"Existing user session is valid",
"User already has a valid session",
}));

tdkLogs.Clear();

yield return TestHelpers.WaitForTask(TDK.Identity.EndUserSession(), 10);
Assert.That(TDK.Identity.IsAuthenticated, Is.False);

yield return ForceFlushCache();

Assert.That(tdkLogs.Count, Is.EqualTo(2));

var eventsArray = AnalyticsEvent.ExtractPayloadFromLog(tdkLogs[0]);
Assert.That(eventsArray.Count, Is.EqualTo(1));
Assert.That(eventsArray[0].name, Is.EqualTo("tc_disconnected"));
Assert.That(tdkLogs[1], Is.EqualTo("[TDKAnalyticsService.IO:SendEvents] Events sent successfully"));
}

private IEnumerator ForceFlushCache()
{
var analyticsService = TDKServiceLocator.GetService<TDKAnalyticsService>();
analyticsService.FlushCache();
yield return new WaitForSeconds(5);
}

[System.Serializable]
class AnalyticsEvent {
public string name;

public static List<AnalyticsEvent> ExtractPayloadFromLog(string logString) {
var payloadLogParts = logString.Split(" Payload:");
Assert.That(payloadLogParts[0], Is.EqualTo("[TDKAnalyticsService.IO:SendEventBatch]"));
var eventsArray = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AnalyticsEvent>>(payloadLogParts[1])!;
return eventsArray;
}
}
}
11 changes: 11 additions & 0 deletions Assets/TestsBasic/IntegrationTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions Assets/TestsBasic/TestHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@


using System;
using System.Collections;
using System.IO;
using System.Threading.Tasks;
using NUnit.Framework;
using Treasure;
using UnityEngine;

public class TestHelpers
{
public class TestTDKAbstractedEngineApi : TDKAbstractedEngineApi
{
const string keyPrefix = "[unitTesting] ";

public override string ApplicationPersistentDataPath()
{
return Path.Combine(base.ApplicationPersistentDataPath(), "Testing");
}

public override T GetPersistedValue<T>(string key)
{
return base.GetPersistedValue<T>(keyPrefix + key);
}

public override void SetPersistedValue<T>(string key, T value)
{
base.SetPersistedValue(keyPrefix + key, value);
}

public override void DeletePersistedValue(string key)
{
base.DeletePersistedValue(keyPrefix + key);
}
}

public static TestTDKAbstractedEngineApi GetTestAbstractedEngineApi() {
return new TestTDKAbstractedEngineApi();
}

public static void ClearPersistedEventBatches(TDKAbstractedEngineApi testTDKAbstractedEngineApi)
{
var path = Path.Combine(
testTDKAbstractedEngineApi.ApplicationPersistentDataPath(),
AnalyticsConstants.PERSISTENT_DIRECTORY_NAME
);
if (Directory.Exists(path))
{
var directoryInfo = new DirectoryInfo(path);

var files = directoryInfo.GetFiles();

foreach (var file in files)
{
file.Delete();
}
} else {
Directory.CreateDirectory(path);
}
}

public static IEnumerator WaitUntilWithMax(Func<bool> predicate, float maxWait) {
float timeout = maxWait;
yield return new WaitUntil(() => {
timeout -= Time.deltaTime;
return timeout <= 0 || predicate();
});
}

public static IEnumerator WaitForTask(Task task, float maxWait = 5) {
yield return WaitUntilWithMax(() => task.IsCompleted, maxWait);
Assert.That(task.IsCompleted);
}
}
11 changes: 11 additions & 0 deletions Assets/TestsBasic/TestHelpers.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c990482

Please sign in to comment.