Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user