using Omika.Data.Repositories; namespace Omika.Tests.Mobile; /// /// Tests de la génération de nom unique des documents côté serveur /// (DocumentPathResolver.WriteUniqueFile) : jamais d'écrasement, nom en base == /// nom physique, écriture atomique anti-collision concurrente, anti path traversal. /// public class DocumentUniqueFileNameTests : IDisposable { private readonly string _dir; public DocumentUniqueFileNameTests() { _dir = Path.Combine(Path.GetTempPath(), "omika-unique", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_dir); } public void Dispose() { if (Directory.Exists(_dir)) { Directory.Delete(_dir, recursive: true); } } [Fact] public void PremierUpload_AucunExistant_ConserveLeNom() { // Act string? name = DocumentPathResolver.WriteUniqueFile(_dir, "Viamichelin.pdf", [1, 2, 3]); // Assert Assert.Equal("Viamichelin.pdf", name); Assert.True(File.Exists(Path.Combine(_dir, "Viamichelin.pdf"))); } [Fact] public void DeuxiemeUpload_MemeNom_GenereSuffixe_EtConserveLePremier() { // Arrange : Viamichelin.pdf existe déjà. File.WriteAllBytes(Path.Combine(_dir, "Viamichelin.pdf"), new byte[] { 1, 1, 1 }); // Act string? name = DocumentPathResolver.WriteUniqueFile(_dir, "Viamichelin.pdf", new byte[] { 2, 2, 2 }); // Assert : le second devient Viamichelin_1.pdf, le premier est intact. Assert.Equal("Viamichelin_1.pdf", name); Assert.Equal(new byte[] { 1, 1, 1 }, File.ReadAllBytes(Path.Combine(_dir, "Viamichelin.pdf"))); Assert.Equal(new byte[] { 2, 2, 2 }, File.ReadAllBytes(Path.Combine(_dir, "Viamichelin_1.pdf"))); } [Fact] public void TroisiemeUpload_DeuxExistants_GenereLeSuffixeSuivant() { // Arrange : Viamichelin.pdf et Viamichelin_1.pdf existent déjà. File.WriteAllBytes(Path.Combine(_dir, "Viamichelin.pdf"), new byte[] { 1 }); File.WriteAllBytes(Path.Combine(_dir, "Viamichelin_1.pdf"), new byte[] { 2 }); // Act string? name = DocumentPathResolver.WriteUniqueFile(_dir, "Viamichelin.pdf", new byte[] { 3 }); // Assert Assert.Equal("Viamichelin_2.pdf", name); Assert.True(File.Exists(Path.Combine(_dir, "Viamichelin_2.pdf"))); } [Fact] public void UploadsConcurrents_MemeNom_ProduisentDesNomsDistincts() { // Act : écritures parallèles du même nom → chacune doit obtenir un nom unique. var results = new string?[8]; Parallel.For(0, results.Length, i => { results[i] = DocumentPathResolver.WriteUniqueFile(_dir, "Viamichelin.pdf", [(byte)i]); }); // Assert : 8 noms distincts (aucun écrasement). Assert.Equal(results.Length, results.Distinct().Count()); Assert.Equal(results.Length, Directory.GetFiles(_dir).Length); } [Fact] public void NomDangereux_PathTraversal_Bloque() { // Act : "..\..\Viamichelin.pdf" doit être réduit au simple nom de fichier. string? name = DocumentPathResolver.WriteUniqueFile(_dir, @"..\..\Viamichelin.pdf", [1, 2, 3]); // Assert : aucun fichier hors du répertoire, le nom est assaini. Assert.Equal("Viamichelin.pdf", name); Assert.True(File.Exists(Path.Combine(_dir, "Viamichelin.pdf"))); Assert.False(File.Exists(Path.Combine(Path.GetDirectoryName(_dir)!, "Viamichelin.pdf"))); } [Fact] public void NomAvecCaracteresInvalides_EstNettoye() { // Act + Assert Assert.Equal("Viami_chel_in.pdf", DocumentPathResolver.SanitizeFileName("Viamiin.pdf")); Assert.Equal("document", DocumentPathResolver.SanitizeFileName(" ")); } }