using Omika.App.Local.Models; using Omika.App.Local.Services; namespace Omika.Tests.Mobile; /// /// Tests du jeton de cible des modèles multilingues : lecture du fichier /// modele.json d'un sens, validation (un jeton malformé est refusé, jamais /// ignoré en silence) et composition du texte effectivement envoyé au modèle. /// /// /// Le pipeline ONNX complet n'est pas exerçable ici : il exige un vocabulaire /// SentencePiece réel et le moteur d'inférence. La preuve de bout en bout se fait /// avec Omika.LanguesProbe traduire --dossier <pack>, qui exécute le /// vrai moteur sur un vrai pack. /// /// public class ModelePackMetadonneesTests { [Fact] public void DepuisJson_JetonPresent_EstLuAvecSonModele() { string json = """ { "version": 1, "modeleSource": "Helsinki-NLP/opus-mt-tc-big-itc-itc", "famille": "itc", "jetonCible": ">>fra<<" } """; ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson(json); Assert.NotNull(metadonnees); Assert.True(metadonnees.AjouteUnJeton); Assert.Equal(">>fra<<", metadonnees.JetonCible); Assert.Equal("Helsinki-NLP/opus-mt-tc-big-itc-itc", metadonnees.ModeleSource); Assert.Equal("itc", metadonnees.Famille); Assert.Equal(1, metadonnees.Version); } [Fact] public void DepuisJson_SansJeton_EstUnModeleDedie() { string json = """{ "modeleSource": "Helsinki-NLP/opus-mt-es-fr" }"""; ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson(json); Assert.NotNull(metadonnees); Assert.False(metadonnees.AjouteUnJeton); Assert.Equal(string.Empty, metadonnees.JetonCible); } [Fact] public void DepuisJson_VersionAbsente_VautUn() { ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson("""{ "jetonCible": ">>fra<<" }"""); Assert.NotNull(metadonnees); Assert.Equal(1, metadonnees.Version); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void DepuisJson_ContenuVide_EstTraiteCommeAbsent(string? json) { Assert.Null(ModelePackMetadonnees.DepuisJson(json)); } [Fact] public void DepuisJson_JetonEntoureDEspaces_EstNettoye() { ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson("""{ "jetonCible": " >>fra<< " }"""); Assert.NotNull(metadonnees); Assert.Equal(">>fra<<", metadonnees.JetonCible); } [Fact] public void DepuisJson_JetonAvecEspaceInterne_EstRefuse() { // Un jeton contenant un espace serait découpé par le tokeniseur : le modèle // ne le reconnaîtrait pas et produirait sa langue par défaut, sans erreur. FormatException ex = Assert.Throws( () => ModelePackMetadonnees.DepuisJson("""{ "jetonCible": ">>fra<< x" }""")); Assert.Contains("espace", ex.Message, StringComparison.OrdinalIgnoreCase); } [Fact] public void DepuisJson_JetonTropLong_EstRefuse() { string jeton = ">>" + new string('a', ModelePackMetadonnees.LongueurMaxJeton) + "<<"; FormatException ex = Assert.Throws( () => ModelePackMetadonnees.DepuisJson($$"""{ "jetonCible": "{{jeton}}" }""")); Assert.Contains("trop long", ex.Message, StringComparison.OrdinalIgnoreCase); } [Fact] public void DepuisJson_VersionFuture_EstRefusee() { // Une version inconnue peut changer le SENS du champ : la lire à l'aveugle // reviendrait à deviner. FormatException ex = Assert.Throws( () => ModelePackMetadonnees.DepuisJson("""{ "version": 99, "jetonCible": ">>fra<<" }""")); Assert.Contains("version", ex.Message, StringComparison.OrdinalIgnoreCase); } [Fact] public void DepuisJson_JsonInvalide_EstRefuse() { Assert.Throws(() => ModelePackMetadonnees.DepuisJson("{ pas du json")); } [Fact] public void DepuisJson_ProprieteInconnue_EstIgnoree() { // Compatibilité ascendante : un pack plus récent peut ajouter des champs. ModelePackMetadonnees? metadonnees = ModelePackMetadonnees.DepuisJson( """{ "jetonCible": ">>fra<<", "champFutur": 42 }"""); Assert.NotNull(metadonnees); Assert.Equal(">>fra<<", metadonnees.JetonCible); } [Fact] public void InsererJetonCible_AvecIdentifiant_LePlaceEnTete() { // Valeurs réelles d'opus-mt-tc-big-itc-itc : >>fra<< = 14666, et la suite // d'identifiants du modèle (jamais ceux du .spm). IReadOnlyList ids = OnnxTranslationService.InsererJetonCible([6636, 5, 7228], 14666); Assert.Equal([14666, 6636, 5, 7228], ids); } [Fact] public void InsererJetonCible_SansIdentifiant_RendLaSequenceTelleQuelle() { IReadOnlyList ids = OnnxTranslationService.InsererJetonCible([6636, 5], -1); Assert.Equal([6636, 5], ids); } [Fact] public void InsererJetonCible_NeModifiePasLaSequenceDOrigine() { int[] origine = [6636, 5]; IReadOnlyList resultat = OnnxTranslationService.InsererJetonCible(origine, 14666); Assert.Equal([6636, 5], origine); Assert.NotSame(origine, resultat); } }