using RudderSdk.Core; using RudderSdk.Core.Abstractions; using RudderSdk.Core.Models.Auth; using Xunit; namespace RudderSdk.Core.Tests; public sealed class RudderClientTests { [Fact] public void Constructor_Composes_All_Runtime_Services() { var client = CreateClient(); Assert.NotNull(client.Auth); Assert.NotNull(client.Player); Assert.NotNull(client.RemoteConfig); Assert.NotNull(client.Storage); Assert.NotNull(client.Stores); Assert.NotNull(client.Leaderboards); Assert.NotNull(client.Inventory); Assert.NotNull(client.BattlePass); Assert.NotNull(client.Quests); Assert.NotNull(client.Scenario); Assert.NotNull(client.Effects); } [Fact] public void Constructor_Requires_BaseUrl_And_ProjectKey() { Assert.Throws(() => new RudderClient(new RudderClientOptions { ProjectKey = "key" })); Assert.Throws(() => new RudderClient(new RudderClientOptions { BaseUrl = "http://localhost" })); } [Fact] public void Constructor_Uses_HttpClientTransport_By_Default() { var client = new RudderClient(new RudderClientOptions { BaseUrl = "http://localhost:8082", ProjectKey = "project-key" }); Assert.NotNull(client); } [Fact] public async Task Auth_LoginWithDevice_Saves_Tokens_And_Raises_SignedIn() { var transport = new FakeTransport(); transport.Responses[typeof(LoginViaDeviceResponse)] = new LoginViaDeviceResponse { AccessToken = "access-token", RefreshToken = "refresh-token" }; var tokenStore = new FakeTokenStore(); var client = CreateClient(transport: transport, tokenStore: tokenStore); var states = new List(); client.Auth.AuthStateChanged += states.Add; await client.Auth.LoginWithDeviceAsync("en", "en", "nickname"); var request = Assert.IsType(transport.LastRequest); Assert.Equal("POST", transport.LastMethod); Assert.Equal("/sdk/v1/authorization/device", transport.LastPath); Assert.Equal("nickname", request.Nickname); Assert.Equal("access-token", tokenStore.AccessToken); Assert.Equal("refresh-token", tokenStore.RefreshToken); Assert.Equal(new[] { RudderAuthState.SignedIn }, states); } [Fact] public void Auth_Logout_Clears_Tokens_And_Raises_SignedOut() { var tokenStore = new FakeTokenStore { AccessToken = "a", RefreshToken = "r" }; var client = CreateClient(tokenStore: tokenStore); var states = new List(); client.Auth.AuthStateChanged += states.Add; client.Auth.Logout(); Assert.Null(tokenStore.AccessToken); Assert.Null(tokenStore.RefreshToken); Assert.Equal(new[] { RudderAuthState.SignedOut }, states); } private static RudderClient CreateClient( FakeTransport? transport = null, FakeTokenStore? tokenStore = null) { return new RudderClient(new RudderClientOptions { BaseUrl = "http://localhost:8082", ProjectKey = "project-key", Transport = transport ?? new FakeTransport(), TokenStore = tokenStore ?? new FakeTokenStore(), DeviceIdProvider = new FakeDeviceIdProvider() }); } internal sealed class FakeTransport : IRudderTransport { public Dictionary Responses { get; } = new(); public string? LastMethod { get; private set; } public string? LastPath { get; private set; } public object? LastRequest { get; private set; } public string? LastAccessToken { get; private set; } public Task SendAsync( string method, string path, TRequest? request, string? accessToken, CancellationToken cancellationToken = default) { LastMethod = method; LastPath = path; LastRequest = request; LastAccessToken = accessToken; if (Responses.TryGetValue(typeof(TResponse), out var response)) return Task.FromResult((TResponse)response); return Task.FromResult(default(TResponse)!); } } internal sealed class FakeTokenStore : ITokenStore { public string? AccessToken { get; set; } public string? RefreshToken { get; set; } public string? GetAccessToken() => AccessToken; public string? GetRefreshToken() => RefreshToken; public void SaveTokens(string accessToken, string refreshToken) { AccessToken = accessToken; RefreshToken = refreshToken; } public void Clear() { AccessToken = null; RefreshToken = null; } } private sealed class FakeDeviceIdProvider : IDeviceIdProvider { public string DeviceId => "device-id"; } }