using BuildingBlock.Identity.Contracts.Conventions; using BuildingBlock.Identity.Contracts.Requests; using BuildingBlock.Identity.Contracts.Responses; using Thalos.Service.Application.Oidc; using Thalos.Service.Application.Sessions; using Thalos.Service.Application.UseCases; using Thalos.Service.Identity.Abstractions.Contracts; using ExchangeRequest = BuildingBlock.Identity.Contracts.Requests.ExchangeIdentityProviderTokenRequest; using ExchangeResponse = BuildingBlock.Identity.Contracts.Responses.ExchangeIdentityProviderTokenResponse; using IdentityIssueRequest = BuildingBlock.Identity.Contracts.Requests.IssueIdentityTokenRequest; using IdentityIssueResponse = BuildingBlock.Identity.Contracts.Responses.IssueIdentityTokenResponse; namespace Thalos.Service.Application.UnitTests; public class StartIdentitySessionUseCaseTests { [Fact] public async Task HandleAsync_WhenCalled_IssuesTokenAndRefreshToken() { var issueUseCase = new FakeIssueUseCase(); var useCase = new StartIdentitySessionUseCase( issueUseCase, new FakeExchangeService(), new FakeSessionTokenCodec()); var response = await useCase.HandleAsync(new StartIdentitySessionRequest("user-1", "tenant-1", IdentityAuthProvider.InternalJwt)); Assert.Equal("token-abc", response.AccessToken); Assert.Equal(1800, response.ExpiresInSeconds); Assert.Equal("user-1", response.SubjectId); Assert.Equal("tenant-1", response.TenantId); Assert.Equal("refresh-user-1-tenant-1", response.RefreshToken); Assert.NotNull(issueUseCase.LastRequest); Assert.Equal("user-1", issueUseCase.LastRequest!.SubjectId); } [Fact] public async Task HandleAsync_WhenGoogleProviderAndExchangeSucceeds_UsesExchangedSubject() { var issueUseCase = new FakeIssueUseCase(); var exchangeService = new FakeExchangeService { NextResponse = new ExchangeResponse( "google-sub-123", "tenant-1", IdentityAuthProvider.Google, true) }; var useCase = new StartIdentitySessionUseCase( issueUseCase, exchangeService, new FakeSessionTokenCodec()); var response = await useCase.HandleAsync(new StartIdentitySessionRequest( string.Empty, "tenant-1", IdentityAuthProvider.Google, "google-id-token", "corr-1")); Assert.Equal("token-abc", response.AccessToken); Assert.Equal("google-sub-123", response.SubjectId); Assert.Equal(IdentityAuthProvider.Google, response.Provider); Assert.NotNull(issueUseCase.LastRequest); Assert.Equal("google-sub-123", issueUseCase.LastRequest!.SubjectId); Assert.Equal("google-id-token", issueUseCase.LastRequest.ExternalToken); } [Fact] public async Task HandleAsync_WhenGoogleExchangeFails_ReturnsFailedSession() { var issueUseCase = new FakeIssueUseCase(); var exchangeService = new FakeExchangeService { NextResponse = new ExchangeResponse( string.Empty, "tenant-1", IdentityAuthProvider.Google, false) }; var useCase = new StartIdentitySessionUseCase( issueUseCase, exchangeService, new FakeSessionTokenCodec()); var response = await useCase.HandleAsync(new StartIdentitySessionRequest( string.Empty, "tenant-1", IdentityAuthProvider.Google, "invalid-token", "corr-2")); Assert.Equal(string.Empty, response.AccessToken); Assert.Equal(string.Empty, response.RefreshToken); Assert.Equal(0, response.ExpiresInSeconds); Assert.Null(issueUseCase.LastRequest); } private sealed class FakeIssueUseCase : IIssueIdentityTokenUseCase { public IdentityIssueRequest? LastRequest { get; private set; } public Task HandleAsync(IdentityIssueRequest request) { LastRequest = request; return Task.FromResult(new IdentityIssueResponse("token-abc", 1800)); } } private sealed class FakeExchangeService : IIdentityProviderTokenExchangeService { public ExchangeResponse NextResponse { get; set; } = new( string.Empty, string.Empty, IdentityAuthProvider.Google, false); public Task ExchangeAsync(ExchangeRequest request, CancellationToken cancellationToken = default) { return Task.FromResult(NextResponse); } } private sealed class FakeSessionTokenCodec : IIdentitySessionTokenCodec { public string Encode(IdentitySessionDescriptor descriptor) { return $"refresh-{descriptor.SubjectId}-{descriptor.TenantId}"; } public bool TryDecode(string token, out IdentitySessionDescriptor descriptor) { descriptor = new IdentitySessionDescriptor(string.Empty, string.Empty, IdentityAuthProvider.InternalJwt, DateTimeOffset.UtcNow); return false; } } }