Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration tests, logger levels, magicswap tweaks, chain switching improvements #86

Merged
merged 12 commits into from
Jul 26, 2024
Merged
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
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