Les tests traditionnels basés sur des exemples conduisent à une qualité accidentelle

Les tests basés sur des exemples sont la méthode sur laquelle la plupart des équipes logicielles s’appuient encore aujourd’hui. Elle consiste à tester le code à l’aide d’une liste de valeurs d’entrée prédéfinies. Celles-ci sont rédigées par les développeurs ou les ingénieurs d’assurance qualité en fonction de ce qu’ils pensent pouvoir se produire. Le problème est que cette liste n’est jamais complète. Par définition, elle ne peut pas l’être. Vous ne vérifiez que ce que vous pensez déjà pouvoir casser. Tout ce qui sort de ce cadre ? Il n’est pas testé. C’est là que le concept de « qualité accidentelle » entre en jeu : le produit semble stable uniquement parce qu’il n’a pas été exposé à ce que personne n’a pensé à vérifier.

S’appuyer sur cette approche est risqué. Les bogues qui vivent juste en dehors du périmètre des cas de test connus peuvent facilement passer inaperçus et se frayer un chemin jusqu’à la production. Lorsque cela se produit après le lancement, vous perdez du temps, de la confiance et, dans de nombreux cas, la confiance des utilisateurs. La correction de ces bogues est généralement simple, c’est leur détection qui est coûteuse.

Pour les chefs d’entreprise, la leçon est simple. La qualité fondée uniquement sur les connaissances passées n’est pas évolutive. Plus votre produit est complexe, plus vous dépendez des développeurs qui anticipent l’inattendu. Cela ne tient pas la route dans des environnements qui évoluent rapidement et où des inconnues font constamment surface. La solution ne consiste pas à abandonner les tests basés sur des exemples. Ils ont leur place. Mais en tant que méthode autonome, elle est défectueuse de par sa conception. Le coût de cette approche n’est pas seulement technique, il est aussi stratégique. Les cas limites manqués signifient des produits défectueux dans les mains de vos utilisateurs.

Les dirigeants doivent reconnaître que pour garantir la fiabilité des produits, il ne suffit pas d’embaucher de meilleurs testeurs ou de consacrer plus de temps à l’assurance qualité, mais qu’il faut adopter des systèmes plus intelligents qui permettent de dépasser les angles morts de l’être humain.

Les tests génératifs permettent de découvrir des bogues en explorant de vastes espaces de problèmes.

Les tests génératifs, ou tests basés sur les propriétés, commencent à remplacer l’approche traditionnelle dans les équipes d’ingénieurs avant-gardistes. Voici pourquoi : au lieu de tester quelques exemples, ils définissent ce qui doit toujours être vrai dans votre système. Il laisse ensuite le moteur de test faire le travail. Il génère automatiquement un large éventail d’entrées et vérifie si votre code respecte toujours ces propriétés fondamentales.

Cela renverse le modèle de test. Vous n’écrivez plus des tests basés sur des risques connus, vous validez le comportement du système par rapport à tout ce que le moteur peut générer. Et il ne s’arrête pas lorsqu’il échoue. Il identifie l’entrée exacte, parfois en la réduisant au plus petit cas, qui a fait échouer le système. Cela rend le débogage rapide, reproductible et bon marché.

Pour les dirigeants, l’intérêt est évident. Les tests génératifs n’acceptent pas vos limites supposées. Il explore tout l’espace de ce que votre système peut rencontrer dans le monde réel. Ils découvrent les cas limites sans qu’il soit nécessaire que quelqu’un y ait pensé à l’avance. Cela signifie moins de bogues en production, une plus grande confiance à l’échelle et une meilleure préparation à des situations que vous n’aviez même pas envisagées.

Les entreprises opérant dans des environnements à fort enjeu, les systèmes financiers, la logistique, les fuseaux horaires, les API mondiales, sont les plus gagnantes. Ces systèmes ne tolèrent pas bien les défaillances. Si vous pouvez les valider à l’aide d’un large éventail de données réelles, votre risque diminue rapidement.

En bref, les tests génératifs rendent vos tests adaptatifs plutôt que réactifs. Il s’agit d’un changement d’état d’esprit dont de nombreuses équipes ont besoin, mais que peu d’entre elles ont pleinement adopté. Et pour les PDG et les directeurs techniques, investir dans ce changement signifie parier sur une automatisation plus intelligente qui trouve les choses que les humains manquent avant que les clients ne le fassent.

La définition et la vérification des invariants sont essentielles à la robustesse des tests.

Dans les tests génératifs, l’accent n’est plus mis sur l’écriture des cas de test, mais sur la définition de propriétés invariantes, de règles qui doivent toujours rester vraies, quoi qu’il arrive. Ces invariants guident le moteur de test. Il ne s’agit pas de savoir ce qu’un système devrait faire dans quelques scénarios, mais de vérifier si le système obéit toujours à ses principes fondamentaux lorsqu’il est confronté à un large éventail de données d’entrée.

Les exemples sont simples mais puissants. Un système financier doit toujours préserver le solde total lors des transferts de compte. Une API traitant les demandes des utilisateurs ne doit jamais renvoyer une trace de pile ou une erreur de serveur interne en réponse à une entrée non valide, elle doit indiquer à l’utilisateur ce qui n’a pas fonctionné, et non exposer la manière dont le système traite les erreurs. Il ne s’agit pas là de simples préoccupations d’ordre technique. Il s’agit de règles de qualité du produit qui importent à vos utilisateurs. Chaque fois qu’un invariant est violé, quelqu’un est susceptible de vivre une mauvaise expérience.

Pour les dirigeants, il s’agit de construire des systèmes qui préviennent activement les échecs au lieu de se contenter d’y réagir. Les invariants clarifient ce qui importe le plus en éliminant les hypothèses. Une fois qu’ils sont définis, le moteur de test les vérifie systématiquement. Vous ne dépendez plus du fait qu’un ingénieur ait eu la clairvoyance de penser à tous les cas de figure. C’est là que réside la véritable valeur. Cette approche produit des systèmes plus résistants et moins d’attentes déçues.

Et lorsque vous pensez à la fiabilité opérationnelle sur des marchés mondiaux, à des comportements variables, à des fuseaux horaires, à des modèles d’utilisation, vous avez besoin d’être sûr que les hypothèses critiques sont toujours valables, en particulier lorsqu’elles échappent à votre contrôle. Tester directement ces propriétés est l’un des moyens les plus efficaces d’y parvenir.

Les tests génératifs révèlent des bogues cachés dans un code apparemment anodin

Même dans les opérations courantes, de petites imperfections peuvent entraîner des problèmes majeurs. Prenons l’exemple de l’addition en virgule flottante. Il s’agit d’une opération simple que la plupart des développeurs considèrent comme inattaquable. Mais les tests basés sur les propriétés révèlent des failles qui enfreignent des lois mathématiques telles que l’associativité, en raison de la manière dont les nombres à virgule flottante sont traités en binaire. Ces bogues ne sont pas théoriques, ils sont réels. Et ils n’apparaîtraient pas dans les tests basés sur des exemples à moins que vous n’en soyez déjà conscient.

Il ne s’agit pas de pinailler. Si les systèmes financiers ou de planification se comportent silencieusement de manière incohérente dans des conditions spécifiques, cela devient un problème de confiance. Les clients n’expliqueront pas toujours ce qui s’est passé, mais ils en ressentiront les effets : calculs incorrects, délais imprévus, comportement imprévisible.

Pour les dirigeants, le principal enseignement à tirer est que la complexité ne nécessite pas de complexité dans la base de code. Elle peut résulter d’éléments logiques de base se comportant de manière légèrement différente en fonction des données d’entrée. Il s’agit du type de problèmes que vous détectez rarement dans les revues de code ou avec les cas de test standard. Mais les tests génératifs les détectent précisément parce qu’ils ne reposent sur aucune hypothèse.

Vous donnez à votre système une chance de révéler des failles dont vous ne soupçonniez pas l’existence. Et une fois que c’est le cas, le cadre permet d’isoler l’entrée qui a déclenché la défaillance, de manière rapide et reproductible. Pas de conjecture, pas de retour en arrière dans les journaux. Ce niveau de précision se traduit directement par une réduction des frais généraux opérationnels et des boucles d’itération plus rapides. Et lorsque vous multipliez cela par des milliers d’interactions ou des millions d’utilisateurs, l’avantage est évident.

Les tests génératifs permettent de découvrir les défaillances d’exécution au niveau des couches logicielles.

Les tests génératifs ne s’arrêtent pas à la logique du back-end ou aux opérations mathématiques. Il s’applique à toutes les couches d’un système logiciel, des API aux bases de données. Il s’agit là d’un changement de capacité majeur. Dans un cas, cela a permis de mettre en évidence un bogue dans lequel un serveur renvoyait du HTML mal formaté au lieu de JSON valide, simplement parce que l’en-tête « Accept » avait été défini sur une valeur inattendue. Aucun des tests basés sur des exemples ne l’avait détecté. Pourquoi ? Parce que personne n’avait prévu cette configuration de demande spécifique. Mais le système l’a vue, l’a générée et l’a signalée.

Dans un autre cas, il a identifié une erreur de logique subtile dans une requête SQL chargée de vérifier les réunions qui se chevauchent. La requête originale traitait la plupart des scénarios de chevauchement, mais pas tous. Une combinaison particulière où une réunion en engloutissait une autre a été négligée. Là encore, le problème ne résidait pas dans l’effort, mais dans la méthode. Les cas de test conçus par l’homme n’ont pas exploré tous les chemins. Le test génératif l’a fait.

Ce qui importe ici, d’un point de vue commercial, c’est qu’il ne s’agit pas de bogues théoriques. Il s’agit de défauts réels qui apparaîtraient en production lorsque les clients emprunteraient des chemins peu communs, mais toujours valables. Les incohérences dans les réponses de l’API ou les résultats de recherche incorrects des opérations de la base de données affectent la confiance des utilisateurs et la crédibilité du produit. Trouver et corriger ces problèmes avant le déploiement n’est pas seulement une question de qualité technique, c’est aussi un soutien direct à la croissance, à la fidélisation et à l’acquisition de clients.

Les dirigeants qui s’attachent à réduire le volume d’incidents, les tickets d’assistance et les exercices d’évacuation après déploiement devraient considérer cette approche comme une voie claire vers des gains mesurables en termes de temps de fonctionnement et de stabilité de l’expérience client.

Les tests génératifs permettent d’identifier les cas particuliers tels que les lacunes en matière d’heure d’été.

La logique temporelle est délicate, surtout lorsqu’elle s’applique à l’échelle mondiale. Dans de nombreuses régions, les règles d’heure d’été modifient le comportement de l’heure, en ajoutant ou en sautant des heures pendant les transitions. La plupart des développeurs des pays ne connaissant pas ces horaires ne pensent jamais à tester cette logique. Et ils ne devraient pas avoir à le faire. Les systèmes automatisés sont là pour ça.

Lors d’un test réel, les données génératives ont révélé que la création d’une réunion au cours d’une heure d’été inexistante déclenchait une défaillance logique. Le système a réinterprété l’heure de début de manière implicite, ce qui a entraîné la fin d’une réunion avant qu’elle ne commence ostensiblement. Cette erreur a été validée et n’est apparue que lorsque les contraintes en aval ont échoué. Un test basé sur un exemple ne l’a pas détecté parce que personne n’avait écrit un cas pour cette date et ce fuseau horaire spécifiques.

Le moteur de test l’a trouvé, non pas par hasard, mais grâce à une exploration structurée et à une vérification cohérente des invariants. Ceci est important car les erreurs temporelles sont très contextuelles et sensibles à l’appareil. Elles peuvent ne se produire que quelques jours ou quelques heures par an, dans une région, sous une configuration donnée. Mais lorsqu’elles se produisent en production, elles sont désordonnées, difficiles à reproduire et nuisent rapidement à la crédibilité, en particulier dans les systèmes basés sur les calendriers, les réservations ou les transactions.

Ce niveau de validation est essentiel pour les produits mondiaux, en particulier ceux qui sont utilisés au-delà des fuseaux horaires, des gouvernements et des systèmes de calendrier. Il ne s’agit pas de couvrir manuellement tous les cas de figure. Il s’agit de savoir que votre système se comporte correctement même lorsque les cas limites apparaissent. L’avantage concurrentiel réside dans le fait d’être préparé à des scénarios que d’autres ne voient pas, et de les attraper là où ils commencent, avant qu’un client ne le fasse.

Les tests génératifs modélisent efficacement les flux de travail complexes à utilisateurs multiples.

Plus votre produit est interconnecté, plus vous introduisez de variables, en particulier lorsque plusieurs utilisateurs interagissent avec des systèmes partagés. Les tests génératifs ne se contentent pas de traiter des entrées isolées ; ils peuvent modéliser des séries d’actions entre différents utilisateurs. Cela signifie qu’il est capable d’explorer des flux de travail entiers, de créer, d’inviter, d’accepter, de rejeter, exécutés par plusieurs personnes, dans des ordres différents et à des moments variables.

Cela a été clairement démontré lorsque les tests génératifs ont révélé un bogue qui permettait le chevauchement des réunions. Dans ce cas, un utilisateur a créé une réunion, puis en a créé une deuxième qui se chevauchait, et a finalement accepté une invitation à la première, se plaçant ainsi dans deux réunions simultanées. Cette séquence semble rare, mais elle est toujours valable. Aucun développeur ou testeur n’a pensé à écrire un scénario de test aussi complexe. Le système l’a fait de manière autonome.

Une autre variante du même bogue s’est produite avec trois utilisateurs. Des actions d’invitation et d’acceptation différentes ont à nouveau conduit un utilisateur à deux réunions qui se chevauchaient. Il ne s’agissait pas d’une erreur isolée. Il s’agissait d’un modèle que le système pouvait découvrir sous plusieurs voies d’entrée, révélant une lacune dans la logique de base que les tests traditionnels n’avaient pas détectée.

D’un point de vue exécutif, cela témoigne d’une précision et d’une profondeur. Les tests génératifs ne s’arrêtent pas à la vérification des composants. Il évalue les conséquences d’un comportement réel dans un système vivant, la façon dont il répond aux flux de travail pilotés par l’utilisateur à travers la concurrence et les changements d’état. C’est essentiel pour les systèmes où les règles de gestion doivent être appliquées de manière cohérente. La possibilité de valider des séquences réelles avant le lancement permet de réduire les échecs post-déploiement, les escalades et les remaniements qui coûtent du temps et de la crédibilité.

La fonction de rétrécissement accélère le débogage en isolant les entrées défaillantes minimales.

Lorsqu’un échec est détecté, la valeur des tests génératifs augmente encore grâce à une fonction appelée « shrinking ». Lorsqu’un bogue est détecté, le système simplifie l’entrée qui l’a provoqué, en supprimant les variables, les actions et le bruit inutiles, jusqu’à ce qu’il ne reste plus que l’entrée minimale qui provoque encore l’échec. Il ne s’agit pas d’une simple commodité. Il s’agit d’un gain de temps considérable.

L’un des exemples montrait un échec de test impliquant 17 actions différentes de l’utilisateur, avec des réunions qui se chevauchaient, des rejets et des événements redondants. En soi, ce type d’entrée est difficile à déboguer. Un trop grand nombre d’interactions rend plus difficile l’identification de ce qui a déclenché l’erreur. Mais le même moteur de test a automatiquement réduit la séquence d’échec à quatre étapes pertinentes. Le résultat est immédiatement compréhensible et exploitable.

Les équipes d’ingénieurs obtiennent ainsi un retour d’information plus rapide et réduisent le risque d’hypothèses inexactes lors du triage. Les développeurs passent moins de temps à deviner et plus de temps à résoudre les problèmes. Et lorsque vous diffusez rapidement des mises à jour sur des systèmes déployés à l’échelle mondiale, cette rapidité est importante.

Pour les dirigeants, c’est le type d’efficacité opérationnelle qui compte. Des corrections plus rapides signifient moins de temps d’arrêt. Des rapports de bogues plus simples permettent d’alléger les cycles d’ingénierie. La réduction du bruit dans la reproduction des erreurs se traduit par une assurance qualité plus rigoureuse. Il s’agit là d’améliorations mesurables du débit et de la stabilité. Et elles n’exigent pas de l’équipe qu’elle travaille plus dur, mais simplement plus intelligemment, en utilisant les bons outils.

Les extensions invariantes permettent d’améliorer progressivement la qualité des logiciels

L’un des aspects les plus puissants des tests génératifs est qu’ils sont évolutifs, non seulement en ce qui concerne la quantité de données qu’ils peuvent traiter, mais aussi en ce qui concerne la manière dont les nouvelles règles du système peuvent être introduites au fil du temps. Une fois que l’infrastructure des tests génératifs est en place, il est facile de l’étendre. Il ne s’agit pas de réécrire des cas de test ou de reproduire des flux entiers. Vous ajoutez de nouveaux invariants, des règles que le système doit toujours suivre, et vous laissez le même moteur les évaluer dans diverses conditions.

Par exemple, après avoir validé le fait qu’aucun utilisateur ne devrait jamais se trouver dans des réunions qui se chevauchent, un autre invariant a été ajouté : chaque réunion doit avoir au moins un participant confirmé. Avec cette simple mise à jour, le moteur de test a continué à explorer tous les flux de travail existants, mais il a désormais recherché les cas où une réunion pouvait devenir vide à la suite d’un rejet de la part de l’utilisateur. Il a trouvé un cas minimal : un utilisateur crée une réunion et la rejette immédiatement. Le système a permis à la réunion de persister, ce qui a conduit à un état qui aurait dû être évité.

Ce niveau de flexibilité profite directement aux dirigeants qui supervisent des logiciels à grande échelle. Au fur et à mesure que les besoins de l’entreprise évoluent, que ce soit en raison de la réglementation, des commentaires des clients ou de l’extension des fonctionnalités, vous avez besoin d’une approche de test qui suive le rythme. Les suites de tests traditionnelles nécessitent des modifications qui prennent du temps. Avec les tests génératifs, vous pouvez évoluer sans interruption. Vous renforcez votre système central en élargissant son ensemble de règles comportementales, et non en retravaillant son échafaudage de test.

Elle permet également à votre stratégie de qualité des produits de rester alignée sur les priorités de votre entreprise. Lorsque de nouveaux besoins en matière de conformité ou d’engagements en matière d’expérience se font jour, vous injectez ces attentes dans vos contrôles invariants de base et les validez automatiquement à travers d’innombrables voies d’utilisation. Le retour sur investissement de ce niveau d’adaptabilité est important, en particulier sur les marchés concurrentiels ou réglementés.

Les tests génératifs contrecarrent les biais cognitifs tels que WYSIATI

La plupart des tests conventionnels sont façonnés par ce que les développeurs savent déjà ou ont déjà expérimenté. Cela crée des angles morts, des zones où personne ne pense à regarder, simplement parce qu’il n’y a pas encore de preuve que quelque chose pourrait mal se passer. Il s’agit d’un biais humain classique : « ce que vous voyez est tout ce qu’il y a » (WYSIATI), décrit par le psychologue Daniel Kahneman. Le risque est que les systèmes soient déclarés stables sur la base d’une visibilité limitée, et non d’une validation complète.

Les tests génératifs remettent fondamentalement en cause ce préjugé. Il ne suppose pas de connaissances préalables. Il définit des règles, puis exécute des systèmes structurés et aléatoires pour les remettre en question. Les erreurs sont découvertes non pas parce que quelqu’un les a anticipées, mais parce que le système a activement essayé de briser sa propre logique. Cela permet de mettre le doigt sur des défauts introduits par des hypothèses ou un manque de conscience, souvent le type même d’erreurs qui sont les plus difficiles à trouver avec les tests conventionnels.

Prenons l’exemple d’un développeur vivant dans un pays où l’heure d’été n’existe pas. Il peut écrire un code qui passe tous les cas de test qu’il considère comme valables, parce qu’il n’est pas confronté à des conflits liés à l’heure d’été. Les tests génératifs recadrent le processus. Ces cas de test sont générés automatiquement, avec un caractère aléatoire structuré, à travers les calendriers, les fuseaux horaires et les configurations. Et lorsque des échecs surviennent, ce n’est pas en raison des connaissances géographiques de quelqu’un, mais parce que l’invariant a été violé.

Pour les dirigeants, cela signifie que vous ne dépendez plus uniquement de l’expérience de votre équipe pour déterminer les limites des tests. Vous comptez sur le système pour explorer des réalités que votre équipe n’a pas encore rencontrées. Votre modèle d’assurance qualité passe ainsi de la réactivité à l’exploration. Dans un environnement où la vitesse, la stabilité et la portée mondiale sont importantes, c’est une décision stratégique qui vaut la peine d’être prise.

Les tests génératifs impliquent des compromis tels que des durées d’exécution plus longues et le non-déterminisme.

Les tests génératifs apportent des avantages significatifs en termes de fiabilité des systèmes et de couverture des tests, mais ils s’accompagnent de compromis que les décideurs doivent prendre en compte. Tout d’abord, le coût d’exécution. Parce que les tests génératifs explorent un large éventail d’entrées pour chaque propriété, leur exécution est plus longue que celle des tests traditionnels basés sur des exemples. Leur exécution à chaque livraison dans les pipelines d’intégration continue (CI) peut ralentir les cycles de construction. Cela affecte la vélocité des développeurs si cela n’est pas géré de manière stratégique.

Deuxièmement, la nature de la génération d’entrées repose sur le hasard guidé par des heuristiques internes et des générateurs d’entrées. Alors que la plupart des cadres avancés, comme Jqwik, stockent la graine aléatoire utilisée dans l’exécution du test, les échecs déclenchés par des combinaisons d’entrées rares peuvent être difficiles à reproduire en dehors du contexte de cette exécution spécifique. Ce non-déterminisme crée un défi opérationnel, en particulier lorsque la reproduction des problèmes est essentielle dans les différents environnements.

Troisièmement, il y a une courbe d’apprentissage dans la définition d’invariants efficaces, en particulier ceux qui sont importants pour les systèmes complexes. La rédaction de propriétés significatives qui expriment le comportement prévu d’un système nécessite de l’expérience et une réflexion approfondie. Les membres de l’équipe habitués à écrire des cas de test simples ont souvent besoin de temps et de formation pour s’orienter vers la définition de propriétés de plus haut niveau, applicables à l’ensemble du système. Il y a aussi l’effort nécessaire pour modéliser les entrées, en particulier lorsqu’il s’agit d’interactions impliquant plusieurs utilisateurs, entités ou acteurs.

Il ne s’agit pas de raisons d’éviter l’approche, mais de considérations relatives à la conception. Pour les équipes dirigeantes, il s’agit d’aligner la stratégie de test sur les objectifs de l’entreprise. Si un retour d’information rapide sur chaque changement de code est essentiel, alors isolez les tests génératifs pour les exécuter à des étapes spécifiques du pipeline, ou exécutez-les tous les soirs. Si la fiabilité du produit est au cœur de votre marque, ou si une seule défaillance inattendue en production est trop coûteuse, alors la profondeur des tests et les informations fournies par les tests génératifs l’emporteront sur la lenteur de l’exécution.

En fin de compte, le test génératif est un investissement stratégique. Il n’élimine pas les boucles de rétroaction rapide, mais il redéfinit où et comment vous appliquez la confiance. Il exige des équipes qu’elles s’engagent dans l’apprentissage et l’infrastructure, mais il porte ses fruits en réduisant les taux de défauts, en accélérant la reprise après un échec et en renforçant la stabilité du produit dans des conditions imprévisibles. Pour les dirigeants qui cherchent à améliorer la résilience des logiciels sans augmenter indéfiniment les équipes d’assurance qualité, il s’agit d’un changement qui mérite d’être priorisé.

Dernières réflexions

Un logiciel de qualité n’est pas le fruit du hasard. Il y parvient en testant les hypothèses sous pression, en éliminant les angles morts et en laissant le système faire ses preuves dans les conditions auxquelles il sera réellement confronté. C’est exactement ce que font les tests génératifs. Il ne s’appuie pas sur ce que votre équipe sait ou devine, mais remet en question ce que votre système garantit.

Pour les dirigeants, il ne s’agit pas de théorie. Il s’agit de réduire les risques, d’accroître la fiabilité et d’augmenter la qualité sans gonfler les effectifs. C’est ainsi que vous passez de la correction réactive à la validation proactive. Cela permet de gagner du temps là où c’est le plus important, avant que les défaillances n’atteignent vos clients.

Investir dans cette approche est un signe de maturité. Vous ne vous contentez pas de livrer plus rapidement, vous renforcez la confiance dans chaque version. Et lorsque les systèmes se comportent de manière prévisible dans les cas extrêmes, les entrées incohérentes et les règles évolutives, votre entreprise se développe sans frictions supplémentaires.

Les tests génératifs ne se contentent pas d’améliorer le produit. Ils améliorent le processus, l’état d’esprit et le résultat. C’est ainsi que vous construisez des logiciels qui évoluent dans la clarté et non dans le chaos.

Alexander Procter

décembre 19, 2025

23 Min