459 lines
21 KiB
C#
459 lines
21 KiB
C#
using Omika.App.Local.Abstractions;
|
|
using Omika.Core.Contracts.Documents;
|
|
using Omika.App.Local.Models;
|
|
using Omika.App.Local.Repositories;
|
|
using Omika.App.Local.Services;
|
|
using Omika.App.Navigation;
|
|
using Omika.App.Services;
|
|
using Omika.App.ViewModels;
|
|
using Omika.Core.Contracts.Common;
|
|
using Omika.Core.Contracts.Mobile.License;
|
|
using Omika.Core.Contracts.Mobile.Sync;
|
|
using Omika.Core.Contracts.Planning;
|
|
|
|
|
|
namespace Omika.Tests.Mobile.Local;
|
|
|
|
/// <summary>
|
|
/// Tests du RootViewModel : décision de démarrage (activation OU Shell),
|
|
/// bascule après activation réussie et comportement offline.
|
|
/// </summary>
|
|
public class RootViewModelTests : LocalTestBase
|
|
{
|
|
private sealed class FakeSyncEngine : ISyncEngine
|
|
{
|
|
public Task<SyncCycleResult> SyncAsync(CancellationToken ct = default)
|
|
=> Task.FromResult(new SyncCycleResult { Executed = true });
|
|
|
|
public Task<SyncCycleResult> PushAsync(CancellationToken cancellationToken = default)
|
|
=> Task.FromResult(new SyncCycleResult { Executed = true });
|
|
}
|
|
|
|
private sealed class FakeDocumentService : IDocumentLocalService
|
|
{
|
|
public Task<IReadOnlyList<InterventionDocumentItem>> GetDocumentsForInterventionAsync(
|
|
Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<InterventionDocumentItem>>([]);
|
|
|
|
public Task<bool> EnsureDownloadedAsync(Guid documentGuid, CancellationToken ct = default)
|
|
=> Task.FromResult(false);
|
|
|
|
public Task<bool> OpenAsync(Guid documentGuid, CancellationToken ct = default)
|
|
=> Task.FromResult(false);
|
|
|
|
public Task<string?> GetCachedFilePathAsync(Guid documentGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<string?>(null);
|
|
|
|
public Task<IReadOnlyList<DocumentNoteItem>> GetNotesForDocumentAsync(Guid documentGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DocumentNoteItem>>([]);
|
|
|
|
public Task<DocumentNoteItem> AddNoteAsync(Guid documentGuid, string contenu, CancellationToken ct = default)
|
|
=> Task.FromResult(new DocumentNoteItem());
|
|
|
|
public Task<InterventionDocumentItem?> AddPhotoAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<InterventionDocumentItem?>(null);
|
|
|
|
public Task<AddPhotosResult> AddPhotosAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult(new AddPhotosResult());
|
|
|
|
public Task<InterventionDocumentItem?> AddPhotoFromCameraAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<InterventionDocumentItem?>(null);
|
|
|
|
public Task<InterventionDocumentItem?> AddDocumentAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<InterventionDocumentItem?>(null);
|
|
|
|
public Task<InterventionDocumentItem?> AddAnnotatedPhotoAsync(Guid interventionGuid, Guid sourceDocumentGuid, byte[] pngBytes, string fileName, CancellationToken ct = default)
|
|
=> Task.FromResult<InterventionDocumentItem?>(null);
|
|
}
|
|
|
|
private sealed class FakeContactsService : IInterventionContactsService
|
|
{
|
|
public Task<IReadOnlyList<InterventionContact>> GetContactsForInterventionAsync(
|
|
Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<InterventionContact>>([]);
|
|
|
|
public Task<InterventionContact?> GetContactByIdAsync(Guid contactGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<InterventionContact?>(null);
|
|
|
|
public Task<IReadOnlyList<InterventionContact>> GetContactsDisponiblesAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<InterventionContact>>([]);
|
|
}
|
|
|
|
private sealed class FakeDossierService : IDossierLocalService
|
|
{
|
|
public Task<IReadOnlyList<DossierListItem>> GetDossiersAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierListItem>>([]);
|
|
|
|
public Task<IReadOnlyList<DossierListItem>> SearchDossiersAsync(string? refInterne, string? titre, string? client, string? statut, string? type, string? refExterne, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierListItem>>([]);
|
|
|
|
public Task<IReadOnlyList<DossierListItem>> GetLastSearchAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierListItem>>([]);
|
|
|
|
public Task ClearLastSearchAsync(CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task<DossierDetail?> GetDossierByIdAsync(Guid dossierGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<DossierDetail?>(null);
|
|
|
|
public Task SetPinnedAsync(Guid dossierGuid, bool isPinned, DossierListItem? item, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task<IReadOnlyList<DossierListItem>> GetPinnedAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierListItem>>([]);
|
|
|
|
public Task<IReadOnlyList<DossierIntervention>> GetDossierInterventionsAsync(Guid dossierGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierIntervention>>([]);
|
|
|
|
public Task<IReadOnlyList<DossierIntervention>> RefreshDossierInterventionsAsync(Guid dossierGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<DossierIntervention>>([]);
|
|
}
|
|
|
|
private sealed class FakeClientService : IClientLocalService
|
|
{
|
|
public Task<IReadOnlyList<ClientListItem>> GetClientsAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ClientListItem>>([]);
|
|
|
|
public Task<ClientDetail?> GetClientByIdAsync(Guid clientGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<ClientDetail?>(null);
|
|
}
|
|
|
|
private sealed class FakeSiteService : ISiteLocalService
|
|
{
|
|
public Task<IReadOnlyList<SiteListItem>> GetSitesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<SiteListItem>>([]);
|
|
|
|
public Task<SiteDetail?> GetSiteByIdAsync(Guid siteGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<SiteDetail?>(null);
|
|
}
|
|
|
|
private sealed class FakeCollaborateurService : ICollaborateurLocalService
|
|
{
|
|
public Task<IReadOnlyList<CollaborateurListItem>> GetCollaborateursAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<CollaborateurListItem>>([]);
|
|
|
|
public Task<CollaborateurDetail?> GetCollaborateurByIdAsync(Guid collaborateurGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<CollaborateurDetail?>(null);
|
|
}
|
|
|
|
private sealed class FakeEquipeService : IEquipeLocalService
|
|
{
|
|
public Task<IReadOnlyList<EquipeListItem>> GetEquipesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<EquipeListItem>>([]);
|
|
|
|
public Task<IReadOnlyList<EquipeListItem>> GetAllEquipesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<EquipeListItem>>([]);
|
|
}
|
|
|
|
private sealed class FakeSuiviTempsService : ISuiviTempsLocalService
|
|
{
|
|
public Task<Guid> CreateAsync(SuiviTempsSyncPayload payload, CancellationToken ct = default)
|
|
=> Task.FromResult(Guid.NewGuid());
|
|
|
|
public Task<SuiviTempsDto?> GetByGuidAsync(Guid suiviTempsGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<SuiviTempsDto?>(null);
|
|
|
|
public Task<IReadOnlyList<SuiviTempsDto>> GetAllAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<SuiviTempsDto>>([]);
|
|
|
|
public Task<IReadOnlyList<SuiviTempsDto>> GetByInterventionAsync(Guid interventionGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<SuiviTempsDto>>([]);
|
|
|
|
public Task<IReadOnlyList<SuiviTempsDto>> GetByRendezVousAsync(Guid rendezVousGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<SuiviTempsDto>>([]);
|
|
|
|
public Task<IReadOnlyList<SuiviTempsDraftEntry>> GetDraftsByRendezVousAsync(Guid rendezVousGuid, CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<SuiviTempsDraftEntry>>([]);
|
|
|
|
public Task CreateDraftAsync(SuiviTempsDraftEntry draft, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task DeleteDraftAsync(Guid rendezVousGuid, string typeTempsKey, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task<bool> HasTypeTempsAsync(Guid rendezVousGuid, string typeTempsKey, CancellationToken ct = default)
|
|
=> Task.FromResult(false);
|
|
|
|
public Task<bool> IsValideAsync(Guid rendezVousGuid, CancellationToken ct = default)
|
|
=> Task.FromResult(false);
|
|
|
|
public Task ValidateRendezVousAsync(Guid rendezVousGuid, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task SaveRendezVousPourBrouillonAsync(AgendaItem rendezVous, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task<IReadOnlyList<AgendaItem>> GetRendezVousAvecBrouillonAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<AgendaItem>>([]);
|
|
|
|
public Task DeleteRendezVousPourBrouillonAsync(Guid rendezVousId, CancellationToken ct = default)
|
|
=> Task.CompletedTask;
|
|
}
|
|
|
|
private sealed class FakeParametreMetierService : IParametreMetierLocalService
|
|
{
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetTypeTempsAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<Omika.Core.Contracts.Configuration.TypeTempsParametresDto?> GetTypeTempsParametresAsync(string typeTempsKey, CancellationToken ct = default)
|
|
=> Task.FromResult<Omika.Core.Contracts.Configuration.TypeTempsParametresDto?>(null);
|
|
|
|
public Task<Omika.Core.Contracts.Configuration.TypeRdvParametresDto?> GetTypeRdvParametresAsync(string typeRdvKey, CancellationToken ct = default)
|
|
=> Task.FromResult<Omika.Core.Contracts.Configuration.TypeRdvParametresDto?>(null);
|
|
|
|
public Task<Omika.Core.Contracts.Configuration.StatutRdvParametresDto?> GetStatutRdvParametresAsync(string statutRdvKey, CancellationToken ct = default)
|
|
=> Task.FromResult<Omika.Core.Contracts.Configuration.StatutRdvParametresDto?>(null);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetInterventionTypesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetInterventionEtatsAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetDossierTypesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetDossierStatutsAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetClientTypesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetClientStatutsAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetSiteTypesAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
|
|
public Task<IReadOnlyList<ReferenceValueDto>> GetTypeSignatairesAsync(CancellationToken cancellationToken = default)
|
|
=> Task.FromResult<IReadOnlyList<ReferenceValueDto>>([]);
|
|
}
|
|
|
|
|
|
private sealed class FakePhoneLauncher : IPhoneLauncher
|
|
{
|
|
public Task<bool> DialAsync(string phoneNumber) => Task.FromResult(true);
|
|
}
|
|
|
|
private sealed class FakeEmailLauncher : IEmailLauncher
|
|
{
|
|
public Task<bool> ComposeAsync(string email) => Task.FromResult(true);
|
|
}
|
|
|
|
private sealed class FakeMapLauncher : IMapLauncher
|
|
{
|
|
public Task<bool> OpenItineraireAsync(string address) => Task.FromResult(true);
|
|
}
|
|
|
|
private sealed class FakeSessionService : IMobileSessionService
|
|
{
|
|
public bool HasValid { get; set; }
|
|
public bool HasExpired { get; set; }
|
|
public LicenseResponse ActivateToReturn { get; set; } = new();
|
|
public SessionRevalidationResult EnsureValidSessionResult { get; set; } =
|
|
new() { Outcome = SessionRevalidationOutcome.NoSession };
|
|
|
|
public Task<bool> HasValidSessionAsync(CancellationToken ct = default) => Task.FromResult(HasValid);
|
|
public Task<bool> HasExpiredSessionAsync(CancellationToken ct = default) => Task.FromResult(HasExpired);
|
|
|
|
public Task<SessionRevalidationResult> EnsureValidSessionAsync(CancellationToken ct = default)
|
|
=> Task.FromResult(EnsureValidSessionResult);
|
|
|
|
public Task<LicenseResponse> ActivateAsync(
|
|
string codeClient, string numeroLicence, CancellationToken ct = default)
|
|
=> Task.FromResult(ActivateToReturn);
|
|
|
|
public Task<LicenseResponse> ValidateAsync(CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<LocalBootstrapState?> GetBootstrapAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<LocalBootstrapState?>(null);
|
|
|
|
public Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<string?>(null);
|
|
|
|
public Task ClearSessionAsync(CancellationToken ct = default) => Task.CompletedTask;
|
|
|
|
public Task EnsureDevelopmentSessionAsync(CancellationToken ct = default) => Task.CompletedTask;
|
|
}
|
|
|
|
private sealed class FakeEnrollmentService : IEnrollmentService
|
|
{
|
|
public Task<MobileEnrollmentConfiguration?> LoadAsync(CancellationToken ct = default)
|
|
=> Task.FromResult<MobileEnrollmentConfiguration?>(null);
|
|
|
|
public Task<EnrollmentResult> EnrollAsync(string qrContent, CancellationToken ct = default)
|
|
=> Task.FromResult(EnrollmentResult.Failed("refusée"));
|
|
|
|
public Task ClearAsync(CancellationToken ct = default) => Task.CompletedTask;
|
|
}
|
|
|
|
private sealed class FakeQrScanner : IEnrollmentQrScanner
|
|
{
|
|
public Task<string?> ScanAsync(IQrScannerPreviewSink? previewSink, CancellationToken ct = default)
|
|
=> Task.FromResult<string?>(null);
|
|
}
|
|
|
|
|
|
private readonly FakeSessionService _session = new();
|
|
private readonly RootViewModel _root;
|
|
|
|
public RootViewModelTests()
|
|
{
|
|
var preferencesRepository = new UiPreferencesRepository(Database);
|
|
var themeService = new ThemeService(preferencesRepository);
|
|
var navigation = new NavigationService();
|
|
var syncEngine = new FakeSyncEngine();
|
|
|
|
IAgendaService agendaService = new AgendaService(
|
|
RendezVousRepository, ClientRepository, SiteRepository, InterventionRepository, CollaborateurRepository, ApiClient, SessionService);
|
|
IInterventionLocalService interventionService = new InterventionLocalService(
|
|
InterventionRepository, ClientRepository, SiteRepository, DossierRepository,
|
|
RendezVousRepository, DocumentMetadataRepository, CollaborateurRepository,
|
|
OutboxService, ReconciliationService, ApiClient, InterventionCacheRepository, ContextInterventionCacheRepository, Database);
|
|
|
|
var pageFactory = new PageViewModelFactory(
|
|
themeService, agendaService, interventionService, new FakeDocumentService(),
|
|
new FakeContactsService(), new FakeDossierService(), new FakeClientService(),
|
|
new FakeSiteService(), new FakeCollaborateurService(), new FakeEquipeService(),
|
|
new FakeSuiviTempsService(), new FakeParametreMetierService(), InterventionRepository, OutboxRepository, null!,
|
|
new FakePhoneLauncher(), new FakeEmailLauncher(), new FakeMapLauncher(), null!, navigation, _session, new PermissionService(),
|
|
syncEngine,
|
|
null!,
|
|
new FakePhotoPolicyProvider(),
|
|
new PauseDejeunerPreferenceService(PreferencesUtilisateurRepository, SessionService),
|
|
new RendezVousAnnulationLocalService(RendezVousRepository, OutboxService, new RendezVousStateNotifier()),
|
|
new RendezVousFinalisationLocalService(RendezVousRepository, new RendezVousStateNotifier()),
|
|
new CompteRenduScopeService(interventionService),
|
|
new RendezVousStateNotifier(),
|
|
null!,
|
|
Omika.Core.Observability.NullTelemetryService.Instance,
|
|
Network,
|
|
new FakeLangueSecondairePreferenceService(),
|
|
new FakeLanguagePackService(),
|
|
new FakeTranslationService());
|
|
|
|
|
|
|
|
var shell = new ShellViewModel(navigation, themeService, syncEngine, pageFactory, _session, new PermissionService(), OutboxService, null!);
|
|
pageFactory.SetShell(shell);
|
|
|
|
var activation = new LicenseActivationViewModel(new FakeEnrollmentService(), new FakeQrScanner());
|
|
_root = new RootViewModel(_session, shell, activation);
|
|
}
|
|
|
|
[Fact]
|
|
public void AuDemarrage_AfficheActivation()
|
|
{
|
|
// Assert : par défaut, on affiche l'activation (pas de flash du Shell).
|
|
Assert.IsType<LicenseActivationViewModel>(_root.CurrentViewModel);
|
|
Assert.True(_root.IsActivationRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SessionValide_AuDemarrage_BasculeVersShell()
|
|
{
|
|
// Arrange
|
|
_session.HasValid = true;
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<ShellViewModel>(_root.CurrentViewModel);
|
|
Assert.False(_root.IsActivationRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SessionInvalide_AuDemarrage_ResteSurActivation()
|
|
{
|
|
// Arrange
|
|
_session.HasValid = false;
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<LicenseActivationViewModel>(_root.CurrentViewModel);
|
|
Assert.True(_root.IsActivationRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SessionExpiree_DansGrace_BasculeVersShell()
|
|
{
|
|
// Arrange : session valide offline (dans la période de grace).
|
|
_session.HasValid = true;
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<ShellViewModel>(_root.CurrentViewModel);
|
|
Assert.False(_root.IsActivationRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActivationReussie_BasculeVersShell()
|
|
{
|
|
// Act
|
|
_root.OnActivationSucceeded();
|
|
|
|
// Assert
|
|
Assert.IsType<ShellViewModel>(_root.CurrentViewModel);
|
|
Assert.False(_root.IsActivationRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Startup_RejetLicenceServeur_AfficheActivationAvecNoticeEtQr()
|
|
{
|
|
// Arrange : le serveur confirme une licence terminalement invalide.
|
|
_session.EnsureValidSessionResult = new SessionRevalidationResult
|
|
{
|
|
Outcome = SessionRevalidationOutcome.InvalidLicense,
|
|
License = new LicenseResponse { IsValid = false, ErrorCode = "E3003", ErrorMessage = "Licence expirée." }
|
|
};
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert : activation, QR disponible (seul parcours), notice posée.
|
|
Assert.IsType<LicenseActivationViewModel>(_root.CurrentViewModel);
|
|
Assert.True(_root.IsActivationRequired);
|
|
Assert.NotNull(_root.Activation.Notice);
|
|
Assert.True(_root.Activation.IsEnrollmentEnabled);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Startup_HorsLigne_HorsGrace_AfficheConnexionRequiseSansQr()
|
|
{
|
|
// Arrange : token expiré, serveur injoignable, hors période de grace.
|
|
_session.EnsureValidSessionResult = new SessionRevalidationResult { Outcome = SessionRevalidationOutcome.TransientError };
|
|
_session.HasValid = false;
|
|
_session.HasExpired = true;
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert : pas de QR, message « Connexion requise ».
|
|
Assert.IsType<LicenseActivationViewModel>(_root.CurrentViewModel);
|
|
Assert.True(_root.IsActivationRequired);
|
|
Assert.False(_root.Activation.IsEnrollmentEnabled);
|
|
Assert.Contains("Connexion requise", _root.Activation.Notice);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Startup_RenouvellementReussi_SessionValide_BasculeShell()
|
|
{
|
|
// Arrange
|
|
_session.EnsureValidSessionResult = new SessionRevalidationResult { Outcome = SessionRevalidationOutcome.Success };
|
|
_session.HasValid = true;
|
|
|
|
// Act
|
|
await _root.EvaluateSessionAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<ShellViewModel>(_root.CurrentViewModel);
|
|
Assert.False(_root.IsActivationRequired);
|
|
}
|
|
}
|