| Propriété | Valeur |
|---|---|
| Catégorie | Contraintes (JustDummies.Constraints) |
| Sévérité | 🟠 Avertissement |
| Activée par défaut | Oui |
Les constantes écrites au site d’appel n’admettent aucune valeur : la chaîne lève une ConflictingAnyConstraintException dès l’exécution de la ligne d’arrangement — soit parce que les fragments ancrés ne peuvent pas tenir dans la longueur déclarée, soit parce qu’une contrainte de caractères déclarée n’admet aucune des valeurs fournies par un OneOf(...).
C’est le cas que l’ADR-0014 ouvre un nouvel onglet nomme explicitement comme celui qu’un analyseur doit porter et que le système de types ne peut pas porter :
Any.String().WithLength(3).StartingWith("ORD-")entre en conflit parce que le préfixe demande quatre caractères, tandis qu’Any.String().WithLength(12).StartingWith("ORD-")est valide ; le site d’appel et les types statiques sont identiques dans les deux cas.
Seule la valeur de l’argument les distingue — c’est ce qui rend le cas value-dependent, et ce qui le place du côté analyseur de la frontière tracée par l’ADR.
Non conforme
Any.String().WithLength(3).StartingWith("ORD-") // le préfixe demande 4 caractères
Any.String().WithMaxLength(6).StartingWith("SKU-").EndingWith("-EUR") // 8 caractères pour un plafond de 6
Any.String().AlphaNumeric().OneOf("ORD-1", "ORD-2") // la famille n'admet aucune des deux
Any.String().OneOf("abc", "def").InUpperCase() // la casse n'admet aucune des deux
Any.String().StartingWith(" ").WithLength(1).NotBlank() // le préfixe la remplit, et ne porte aucun caractère non blanc
Any.String().WithLength(0).NotBlank() // nulle part où en mettre un
Any.String().WithoutNumeric().InLowerCase().OneOf("1", "A") // aucune seule ne vide le réservoir ; ensemble, si
Conforme
Any.String().WithLength(12).StartingWith("ORD-")
Any.String().WithMaxLength(12).StartingWith("SKU-").EndingWith("-EUR")
Any.String().OneOf("ORD-1", "ORD-2") // retirez la famille : les valeurs sont les vôtres
Any.String().AlphaNumeric().OneOf("ORD1", "ORD2")
Any.String().StartingWith("A").WithLength(1).NotBlank() // le préfixe en porte déjà un
Any.String().StartingWith(" ").WithLength(2).NotBlank() // il reste une position de remplissage, c'est tout ce qu'il demande
Any.String().WithoutNumeric().InLowerCase().OneOf("1", "A", "a") // « a » survit aux deux
Les deux vérifications, et ce que la bibliothèque fait réellement
Budget de longueur. Les fragments sont juxtaposés — préfixe + remplissage + valeurs contenues + remplissage + suffixe, sans jamais se chevaucher — de sorte que la longueur qu’ils réclament est la somme de leurs longueurs, et cette somme doit tenir dans la longueur déclarée.
Deux choses entrent dans cette somme à côté des fragments, parce qu’elles entrent dans celle du générateur :
- Un préfixe ou un suffixe répété ne compte qu’une fois. Chacun possède un emplacement unique : redéclarer le même littéral est sans effet, en déclarer un différent est refusé d’emblée —
StartingWith("ORD-").StartingWith("ORD-").WithLength(4)tireORD-.Containings’accumule au contraire, doncContaining("XY").Containing("XY")réclame réellement quatre caractères. NotBlank()se voit devoir une position de remplissage en propre, mais seulement là où aucun littéral ancré ne porte déjà un caractère non blanc.StartingWith(" ").WithLength(1).NotBlank()n’a nulle part où la mettre ;StartingWith("A").WithLength(1).NotBlank()ne réclame rien de plus, le préfixe réglant la garantie.NonEmpty()etNotBlank()posent chacun un plancher de un même sans aucun fragment, doncWithLength(0).NotBlank()est signalé aussi.
C’est l’arithmétique qu’effectue le générateur, et JD030 lit le même plancher au même endroit : l’intervalle que l’une nomme et le budget que l’autre applique ne peuvent pas diverger.
Le budget est ignoré dès qu’un OneOf(...) est déclaré : un ensemble de valeurs terminal change ce à quoi les fragments sont confrontés — ils sont comparés aux valeurs de l’ensemble plutôt que juxtaposés — de sorte qu’OneOf("aba").WithMaxLength(3).Containing("ab").Containing("ba") est légal bien que les fragments totalisent 4 caractères.
Un ensemble de valeurs vidé. Une famille de caractères, un pool personnalisé, une soustraction ou une casse qui n’admet aucune des valeurs écrites par un OneOf(...) constant. Il n’y a pas de remplissage à côté d’un ensemble de valeurs — l’appelant fournit la chaîne entière — donc la contrainte n’a rien en propre à gouverner et se contente de retirer toutes les valeurs, ce que l’exécution refuse à la déclaration. Le remède est de retirer la contrainte : les valeurs sont les vôtres, et elle n’apporte rien.
C’est la contrepartie de l’exemption ci-dessous, et la raison pour laquelle les deux se lisent différemment. Un littéral ancré revendique sa propre région d’une chaîne façonnée tandis que la famille revendique le reste : les deux ne se rencontrent jamais. Un ensemble de valeurs revendique la chaîne entière, donc la région de la famille est cette valeur fournie elle-même, et les deux doivent s’accorder (ADR-0079 ouvre un nouvel onglet).
Signalé uniquement quand toutes les valeurs sont refusées. Un réservoir dont une valeur survit tire encore : c’est un rétrécissement et non une contradiction, et JD029 nomme les valeurs retirées à la sévérité que mérite une chaîne qui fonctionne.
Les contraintes sont pesées ensemble, et pas seulement une à une. Une valeur doit satisfaire chacune d’elles : le réservoir est donc leur intersection — et une intersection peut être vide sans qu’aucun des ensembles qui y entrent ne le soit. WithoutNumeric().InLowerCase().OneOf("1", "A") est refusé par la bibliothèque parce que la première contrainte retire "1" et la seconde "A", alors que chacune prise isolément laisse une valeur debout. Là où une contrainte répond à elle seule de tout le réservoir, elle est nommée seule, car cette phrase dit quoi retirer ; sinon, toutes celles qui refusent au moins une valeur sont nommées ensemble. Celle qui n’en refuse aucune n’y prend aucune part et reste hors de la phrase — la nommer désignerait une contrainte dont le retrait ne change rien. Savoir laquelle des contraintes nommées forme le plus petit ensemble responsable est une couverture d’ensembles, et cette règle n’y va pas (ADR-0046 ouvre un nouvel onglet).
Ce qui n’est pas signalé
Une famille de caractères, une soustraction ou une casse qu’un fragment ancré contredit. Celles-ci contraignent les caractères que le générateur tire, et un fragment fixé par StartingWith, EndingWith ou Containing est un littéral que vous avez écrit, pas un tirage — ainsi AlphaNumeric().StartingWith("ORD-") est légal et produit ORD- suivi de caractères alphanumériques seulement, le tiret n’apparaissant que dans le préfixe (ADR-0079 ouvre un nouvel onglet). Il en va de même pour WithoutNumeric().StartingWith("ORD-1") et pour InLowerCase().StartingWith("ORD-"). Cette règle signalait ces trois cas ; elle ne doit plus le faire, car cela reviendrait à refuser à la compilation une chaîne que l’exécution honore.
- Un fragment ou une longueur non constants.
- Une chaîne répartie sur plusieurs instructions ou variables. La chaîne doit être écrite comme une seule expression : suivre un générateur à travers une variable locale exigerait une analyse de flot, et une règle qui affirme qu’une chaîne est insatisfiable doit voir toutes les contraintes qu’elle porte.
- Un test négatif vérifiant un conflit, où la chaîne illégale constitue tout le corps d’une lambda passée en argument.