Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
using System.IO.Packaging;
|
||||
using System.Text.Json;
|
||||
using DocumentFormat.OpenXml;
|
||||
using DocumentFormat.OpenXml.Packaging;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Omika.App.ViewModels;
|
||||
using Omika.Core.Contracts.Documents;
|
||||
using Omika.Core.Services;
|
||||
|
||||
namespace Omika.Tests.Mobile.Local;
|
||||
|
||||
/// <summary>
|
||||
/// Trace E2E d'une photo de rapport : la valeur serveur (ListeDonnesBD avec
|
||||
/// <c>cheminComplet</c> + <c>notes</c>) doit survivre au roundtrip mobile
|
||||
/// (désérialisation → sélection → sérialisation ListeDonnesMob) puis être
|
||||
/// exploitée par <see cref="RapportPdfService"/> (image + note dans le DOCX).
|
||||
/// </summary>
|
||||
public class RapportPhotoRoundtripTests
|
||||
{
|
||||
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private static byte[] FakePng(int largeur, int hauteur)
|
||||
{
|
||||
byte[] png = new byte[24];
|
||||
png[0] = 0x89; png[1] = 0x50; png[2] = 0x4E; png[3] = 0x47;
|
||||
png[4] = 0x0D; png[5] = 0x0A; png[6] = 0x1A; png[7] = 0x0A;
|
||||
png[12] = 0x49; png[13] = 0x48; png[14] = 0x44; png[15] = 0x52;
|
||||
png[16] = (byte)(largeur >> 24); png[17] = (byte)(largeur >> 16);
|
||||
png[18] = (byte)(largeur >> 8); png[19] = (byte)largeur;
|
||||
png[20] = (byte)(hauteur >> 24); png[21] = (byte)(hauteur >> 16);
|
||||
png[22] = (byte)(hauteur >> 8); png[23] = (byte)hauteur;
|
||||
return png;
|
||||
}
|
||||
|
||||
private static string PhotoJson(string id, string? guid, string nom, string date, string? cheminComplet, string? notes)
|
||||
=> JsonSerializer.Serialize(new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id,
|
||||
guid,
|
||||
nom,
|
||||
date,
|
||||
chemin = "SousDossier",
|
||||
cheminComplet,
|
||||
notes
|
||||
}
|
||||
}, Json);
|
||||
|
||||
private static RapportChampSousElementDto SousElement(string rubrique, string? membreMob, string? type = null)
|
||||
=> new() { Rubrique = rubrique, MembreMob = membreMob, Type = type ?? "ChpSimpleChaine" };
|
||||
|
||||
private static RapportChampDto TableauPhoto(string listeDonnesBD, params RapportChampSousElementDto[] membres)
|
||||
=> new()
|
||||
{
|
||||
Id = "1",
|
||||
Nom = "Photos",
|
||||
Rubrique = "$TABPHOTO$",
|
||||
Type = "Tableau",
|
||||
GroupeApp = "PHOTO_INTER",
|
||||
ListeDonnesBD = listeDonnesBD,
|
||||
ListeMembre = membres
|
||||
};
|
||||
|
||||
private static byte[] CreerDocxAvecTableau(params string[] cellules)
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
|
||||
{
|
||||
MainDocumentPart main = doc.AddMainDocumentPart();
|
||||
main.Document = new Document();
|
||||
Body body = new Body();
|
||||
var table = new Table();
|
||||
var row = new TableRow();
|
||||
foreach (string cellule in cellules)
|
||||
{
|
||||
row.Append(new TableCell(new Paragraph(new Run(new Text(cellule)))));
|
||||
}
|
||||
table.Append(row);
|
||||
body.Append(table);
|
||||
main.Document.Append(body);
|
||||
main.Document.Save();
|
||||
}
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
private static (int ImageParts, int Drawings, string Texte) Inspecter(byte[] docx)
|
||||
{
|
||||
int imageParts;
|
||||
using (var pkgStream = new MemoryStream(docx))
|
||||
using (Package package = Package.Open(pkgStream, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
imageParts = package.GetParts()
|
||||
.Count(p => p.Uri.OriginalString.StartsWith("/word/media/", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
using var stream = new MemoryStream(docx);
|
||||
using WordprocessingDocument doc = WordprocessingDocument.Open(stream, false);
|
||||
MainDocumentPart main = doc.MainDocumentPart!;
|
||||
int drawings = main.Document.Body!.Descendants<Drawing>().Count();
|
||||
string texte = string.Concat(main.Document.Body.Descendants<Text>().Select(t => t.Text));
|
||||
return (imageParts, drawings, texte);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void Photo_Roundtrip_CheminCompletEtNotes_SontConserves()
|
||||
{
|
||||
string chemin = Path.Combine(Path.GetTempPath(), "omika-photo-e2e", Guid.NewGuid().ToString("N"), "photo.png");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(chemin)!);
|
||||
File.WriteAllBytes(chemin, FakePng(320, 240));
|
||||
|
||||
try
|
||||
{
|
||||
string json = PhotoJson("35", Guid.NewGuid().ToString(), "photo123.jpg", "2024-01-01", chemin, "Peinture mur nord");
|
||||
var edition = new RapportChampEdition(TableauPhoto(
|
||||
json,
|
||||
SousElement("$TABPHOTO.IMAGE$", "cheminComplet", "ChpImage"),
|
||||
SousElement("$TABPHOTO.DATEIMAGE$", "saDate"),
|
||||
SousElement("$TABPHOTO.NOTEIMAGE$", "saNotes")));
|
||||
|
||||
// Le mobile sélectionne la photo.
|
||||
edition.Options[0].EstSelectionne = true;
|
||||
string mob = edition.SerialiserValeur()!;
|
||||
|
||||
using JsonDocument doc = JsonDocument.Parse(mob);
|
||||
JsonElement item = doc.RootElement.EnumerateArray().Single();
|
||||
|
||||
// Le chemin physique ABSOLU et la note survivent au roundtrip.
|
||||
Assert.Equal(chemin, item.GetProperty("cheminComplet").GetString());
|
||||
Assert.Equal("Peinture mur nord", item.GetProperty("notes").GetString());
|
||||
Assert.Equal("photo123.jpg", item.GetProperty("nom").GetString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(Path.GetDirectoryName(chemin)!))
|
||||
{
|
||||
Directory.Delete(Path.GetDirectoryName(chemin)!, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Photo_E2E_DocxContientImageEtNote()
|
||||
{
|
||||
string chemin = Path.Combine(Path.GetTempPath(), "omika-photo-e2e", Guid.NewGuid().ToString("N"), "photo.png");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(chemin)!);
|
||||
File.WriteAllBytes(chemin, FakePng(320, 240));
|
||||
|
||||
try
|
||||
{
|
||||
string json = PhotoJson("35", Guid.NewGuid().ToString(), "photo123.jpg", "2024-01-01", chemin, "Peinture mur nord");
|
||||
var edition = new RapportChampEdition(TableauPhoto(
|
||||
json,
|
||||
SousElement("$TABPHOTO.IMAGE$", "cheminComplet", "ChpImage"),
|
||||
SousElement("$TABPHOTO.DATEIMAGE$", "saDate"),
|
||||
SousElement("$TABPHOTO.NOTEIMAGE$", "saNotes")));
|
||||
|
||||
edition.Options[0].EstSelectionne = true;
|
||||
|
||||
// Ce que le mobile envoie au serveur (ListeDonnesMob).
|
||||
var champMob = new RapportChampDto
|
||||
{
|
||||
Id = "1",
|
||||
Rubrique = "$TABPHOTO$",
|
||||
Type = "Tableau",
|
||||
ListeDonnesMob = edition.SerialiserValeur(),
|
||||
ListeMembre =
|
||||
[
|
||||
SousElement("$TABPHOTO.IMAGE$", "cheminComplet", "ChpImage"),
|
||||
SousElement("$TABPHOTO.DATEIMAGE$", "saDate"),
|
||||
SousElement("$TABPHOTO.NOTEIMAGE$", "saNotes")
|
||||
]
|
||||
};
|
||||
|
||||
byte[] docx = CreerDocxAvecTableau("$TABPHOTO.IMAGE$", "$TABPHOTO.DATEIMAGE$", "$TABPHOTO.NOTEIMAGE$");
|
||||
byte[] resultat = RapportPdfService.RemplirRubriques(docx, new[] { champMob });
|
||||
var (imageParts, drawings, texte) = Inspecter(resultat);
|
||||
|
||||
// L'image est réellement insérée (ImagePart + Drawing) et la note présente.
|
||||
Assert.Equal(1, imageParts);
|
||||
Assert.Equal(1, drawings);
|
||||
Assert.Contains("2024-01-01", texte);
|
||||
Assert.Contains("Peinture mur nord", texte);
|
||||
Assert.DoesNotContain("$TABPHOTO", texte);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(Path.GetDirectoryName(chemin)!))
|
||||
{
|
||||
Directory.Delete(Path.GetDirectoryName(chemin)!, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Photo_NoteNomDifferents_MappingCorrect()
|
||||
{
|
||||
// Nom ≠ note : l'UI ne doit jamais utiliser le nom de fichier comme note.
|
||||
string json = PhotoJson("35", Guid.NewGuid().ToString(), "photo123.jpg", "2024-01-01", null, "Peinture mur nord");
|
||||
var edition = new RapportChampEdition(TableauPhoto(json, SousElement("$TABPHOTO.NOTEIMAGE$", "saNotes")));
|
||||
|
||||
TableauOptionEdition option = Assert.Single(edition.Options);
|
||||
Assert.Equal("photo123.jpg", option.NomPhoto);
|
||||
Assert.Equal("Peinture mur nord", option.NotePhoto);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Photo_NoteAbsente_NomJamaisUtiliseCommeNote()
|
||||
{
|
||||
// Aucune note : la note est vide (jamais le nom de fichier).
|
||||
string json = PhotoJson("35", Guid.NewGuid().ToString(), "photo123.jpg", "2024-01-01", null, null);
|
||||
var edition = new RapportChampEdition(TableauPhoto(json, SousElement("$TABPHOTO.NOTEIMAGE$", "saNotes")));
|
||||
|
||||
TableauOptionEdition option = Assert.Single(edition.Options);
|
||||
Assert.Equal("photo123.jpg", option.NomPhoto);
|
||||
Assert.Null(option.NotePhoto);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user