Files
Omika/tests/Omika.Tests/Mobile/Local/PhotoOptimizationFakes.cs
T
2026-10-23 18:46:50 +02:00

156 lines
5.6 KiB
C#

using Omika.App.Local.Abstractions;
using Omika.Core.Constants;
using Omika.Core.Interfaces;
namespace Omika.Tests.Mobile.Local;
/// <summary>
/// Double de test de <see cref="IImageOptimizer"/>.
///
/// Par défaut il reproduit le comportement « aucune optimisation » : le fichier
/// source est copié à l'identique vers la destination et le résultat indique que
/// rien n'a été modifié. C'est le comportement attendu pour une image déjà sous
/// la limite, et cela permet aux tests existants de rester valides.
///
/// Le test peut demander une optimisation simulée via <see cref="OptimizedBytes"/>.
/// </summary>
public sealed class FakeImageOptimizer : IImageOptimizer
{
/// <summary>Nombre d'appels reçus.</summary>
public int Calls { get; private set; }
/// <summary>Dernière limite reçue, en kilo-octets.</summary>
public int LastMaxSizeKb { get; private set; } = -1;
/// <summary>
/// Contenu à écrire lorsque l'optimisation est simulée. <see langword="null"/>
/// signifie « copier la source telle quelle ».
/// </summary>
public byte[]? OptimizedBytes { get; set; }
/// <summary>Qualité à reporter lorsque l'optimisation est simulée.</summary>
public int? ReportedQuality { get; set; } = 78;
/// <inheritdoc />
public Task<ImageOptimizationResult> OptimizeFileAsync(
string sourcePath,
string destinationPath,
string? fileName,
string? mimeType,
int maxSizeKb,
CancellationToken cancellationToken = default)
{
Calls++;
LastMaxSizeKb = maxSizeKb;
long originalBytes = new FileInfo(sourcePath).Length;
if (OptimizedBytes is not null)
{
File.WriteAllBytes(destinationPath, OptimizedBytes);
return Task.FromResult(new ImageOptimizationResult
{
DestinationPath = destinationPath,
WasOptimized = true,
OriginalBytes = originalBytes,
FinalBytes = OptimizedBytes.LongLength,
OriginalWidth = 4000,
OriginalHeight = 3000,
FinalWidth = 2000,
FinalHeight = 1500,
Quality = ReportedQuality
});
}
File.Copy(sourcePath, destinationPath, overwrite: true);
return Task.FromResult(new ImageOptimizationResult
{
DestinationPath = destinationPath,
WasOptimized = false,
OriginalBytes = originalBytes,
FinalBytes = originalBytes
});
}
}
/// <summary>
/// Double de test de <see cref="IPhotoPolicyProvider"/> : retourne une limite
/// fixe, configurable par le test.
/// </summary>
public sealed class FakePhotoPolicyProvider : IPhotoPolicyProvider
{
/// <summary>Limite retournée, en kilo-octets (0 = aucune limite).</summary>
public int MaxSizeKb { get; set; } = 500;
/// <summary>Origine retournée.</summary>
public PhotoLimitSource Source { get; set; } = PhotoLimitSource.ApplicationParameter;
/// <inheritdoc />
public Task<PhotoLimitResolution> GetResolutionAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(new PhotoLimitResolution(MaxSizeKb, Source));
}
/// <summary>
/// Double de test de <see cref="IApplicationParameterRepository"/> adossé à un
/// dictionnaire en mémoire.
/// </summary>
public sealed class FakeApplicationParameterRepository : IApplicationParameterRepository
{
/// <summary>Valeurs des paramètres applicatifs, indexées par code.</summary>
public Dictionary<string, string?> Values { get; } = new(StringComparer.Ordinal);
/// <inheritdoc />
public Task<string?> GetValueAsync(string code, CancellationToken cancellationToken = default)
{
Values.TryGetValue(code, out string? value);
return Task.FromResult(value);
}
}
/// <summary>
/// Double de test de <see cref="IDocumentDateResolver"/>.
///
/// Par défaut il reproduit le cas « aucune métadonnée fiable » : la date système
/// est retournée avec la source <see cref="DocumentDateSource.SystemFallback"/>,
/// ce qui correspond au comportement des tests existants (aucun fichier réel
/// porteur d'EXIF). Le test peut fixer une date explicite avec
/// <see cref="ResolvedDate"/>, ou simuler un lecteur en échec avec
/// <see cref="ThrowOnResolve"/>.
/// </summary>
public sealed class FakeDocumentDateResolver : IDocumentDateResolver
{
/// <summary>Nombre d'appels reçus.</summary>
public int Calls { get; private set; }
/// <summary>Dernier chemin reçu.</summary>
public string? LastPath { get; private set; }
/// <summary>Date retournée lorsque la propriété est fixée.</summary>
public DateTime? ResolvedDate { get; set; }
/// <summary>Origine retournée (utilisée seulement si <see cref="ResolvedDate"/> est fixée).</summary>
public DocumentDateSource Source { get; set; } = DocumentDateSource.ExifDateTimeOriginal;
/// <summary>Simule un lecteur en échec : la résolution lève une exception.</summary>
public bool ThrowOnResolve { get; set; }
/// <inheritdoc />
public Task<DocumentDateResolution> ResolveAsync(
string filePath,
string? fileName,
CancellationToken cancellationToken = default)
{
Calls++;
LastPath = filePath;
if (ThrowOnResolve)
{
throw new IOException("Métadonnées illisibles (simulation de test).");
}
return Task.FromResult(ResolvedDate is { } value
? new DocumentDateResolution(value, Source)
: new DocumentDateResolution(DateTime.Now, DocumentDateSource.SystemFallback));
}
}