{
  "$schema": "../cases.schema.json",
  "format": 1,
  "domain": "cnh",
  "title": {
    "en": "Driver's license (CNH)",
    "pt-BR": "CNH"
  },
  "functions": [
    {
      "id": "cnh.format",
      "level": "extended",
      "summary": "Formats a Brazilian CNH number.",
      "description": "Formats a CNH number as `000000000-00` (9 digits, hyphen, 2 check digits).\n\n- `options.pad` left-pads the value with zeros to 11 digits first.\n- `options.obfuscate` (default `false`) hides the first 3 digits and the 2 check digits with `*`, after padding: `***503064-**`. It works with `pad` and on a partial value. Like `pad`, it is read for truthiness: a non-boolean such as `1` hides the digits too, and `0` does not.\n- No authority publishes a masking rule for the CNH. The rule is an analogy with the one the Leis de Diretrizes Orçamentárias set for publishing a CPF (\"ocultar os três primeiros dígitos e os dois dígitos verificadores\", Lei nº 12.309/2010, art. 87, § 5º, repeated up to the LDO 2026, Lei nº 15.321/2025, art. 163), not a published norm.\n- `cns.format` and `passport.format` have no `obfuscate` option.\n- A number is read only when it is a safe non-negative integer. A negative, fractional, non-finite or unsafe number returns an empty string (2.4.0 read the digits of any number, sign and decimal point dropped).\n- A value with no digits (empty, or only letters and symbols) returns an empty string even with `options.pad`. Until 2.4.0 `pad` returned the full zero mask (`000000000-00`).\n- Digits after the 11th are dropped.",
      "params": [
        {
          "name": "value",
          "type": "string | number"
        },
        {
          "name": "options",
          "type": "FormatCnhOptions",
          "optional": true
        }
      ],
      "returns": "string",
      "cases": [
        {
          "id": "cnh.format#[\"00000000119\"]",
          "args": [
            "00000000119"
          ],
          "expect": {
            "returns": "000000001-19"
          }
        },
        {
          "id": "cnh.format#remasks",
          "args": [
            "000.000.001-19"
          ],
          "expect": {
            "returns": "000000001-19"
          }
        },
        {
          "id": "cnh.format#partial",
          "args": [
            "0000000011"
          ],
          "expect": {
            "returns": "000000001-1"
          }
        },
        {
          "id": "cnh.format#empty",
          "args": [
            ""
          ],
          "expect": {
            "returns": ""
          }
        },
        {
          "id": "cnh.format#[\"0\"]",
          "args": [
            "0"
          ],
          "expect": {
            "returns": "0"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"00\"]",
          "args": [
            "00"
          ],
          "expect": {
            "returns": "00"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"000\"]",
          "args": [
            "000"
          ],
          "expect": {
            "returns": "000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"0000\"]",
          "args": [
            "0000"
          ],
          "expect": {
            "returns": "0000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"00000\"]",
          "args": [
            "00000"
          ],
          "expect": {
            "returns": "00000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"000000\"]",
          "args": [
            "000000"
          ],
          "expect": {
            "returns": "000000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"0000000\"]",
          "args": [
            "0000000"
          ],
          "expect": {
            "returns": "0000000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"00000000\"]",
          "args": [
            "00000000"
          ],
          "expect": {
            "returns": "00000000"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"000000001\"]",
          "args": [
            "000000001"
          ],
          "expect": {
            "returns": "000000001"
          },
          "note": "JavaScript's own test: should format CNH values"
        },
        {
          "id": "cnh.format#[\"02650306461\",{\"obfuscate\":true}]",
          "args": [
            "02650306461",
            {
              "obfuscate": true
            }
          ],
          "expect": {
            "returns": "***503064-**"
          },
          "note": "from the maintainer briefing, #567"
        },
        {
          "id": "cnh.format#[\"98765432119\",{\"obfuscate\":true}]",
          "args": [
            "98765432119",
            {
              "obfuscate": true
            }
          ],
          "expect": {
            "returns": "***654321-**"
          },
          "note": "JavaScript's own test: should hide the first 3 digits and the 2 check digits when obfuscate is truthy"
        },
        {
          "id": "cnh.format#[98765432119,{\"obfuscate\":true}]",
          "args": [
            98765432119,
            {
              "obfuscate": true
            }
          ],
          "expect": {
            "returns": "***654321-**"
          },
          "note": "JavaScript's own test: should hide the first 3 digits and the 2 check digits when obfuscate is truthy"
        },
        {
          "id": "cnh.format#[\"9876\",{\"obfuscate\":true}]",
          "args": [
            "9876",
            {
              "obfuscate": true
            }
          ],
          "expect": {
            "returns": "***6"
          },
          "note": "JavaScript's own test: should hide the first 3 digits and the 2 check digits when obfuscate is truthy"
        },
        {
          "id": "cnh.format#[\"9876\",{\"pad\":true,\"obfuscate\":true}]",
          "args": [
            "9876",
            {
              "pad": true,
              "obfuscate": true
            }
          ],
          "expect": {
            "returns": "***000098-**"
          },
          "note": "JavaScript's own test: should hide the first 3 digits and the 2 check digits when obfuscate is truthy"
        },
        {
          "id": "cnh.format#[\"98765432119\",{\"obfuscate\":false}]",
          "args": [
            "98765432119",
            {
              "obfuscate": false
            }
          ],
          "expect": {
            "returns": "987654321-19"
          },
          "note": "JavaScript's own test: should behave exactly as without the option when obfuscate is falsy"
        },
        {
          "id": "cnh.format#[\"8900\",{\"pad\":true}]",
          "args": [
            "8900",
            {
              "pad": true
            }
          ],
          "expect": {
            "returns": "000000089-00"
          },
          "note": "JavaScript's documented example: pad"
        },
        {
          "id": "cnh.format#[-1]",
          "args": [
            -1
          ],
          "expect": {
            "returns": ""
          },
          "note": "changed in 2.5.0 (#593): a negative number is not a safe non-negative integer. 2.4.0 returned \"1\". JavaScript's own test: when it is a negative, fractional or unsafe number"
        },
        {
          "id": "cnh.format#[1.5]",
          "args": [
            1.5
          ],
          "expect": {
            "returns": ""
          },
          "note": "changed in 2.5.0 (#593): a fractional number gives an empty string. 2.4.0 returned \"15\". JavaScript's own test: when it is a negative, fractional or unsafe number"
        },
        {
          "id": "cnh.format#[9007199254740992]",
          "args": [
            9007199254740992
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: when it is a negative, fractional or unsafe number (2^53)"
        },
        {
          "id": "cnh.format#[9007199254740992,{\"pad\":true}]",
          "args": [
            9007199254740992,
            {
              "pad": true
            }
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: when it is a negative, fractional or unsafe number"
        },
        {
          "id": "cnh.format#[\"\",{\"pad\":true}]",
          "args": [
            "",
            {
              "pad": true
            }
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: should return an empty string for a value without digits even when padding"
        },
        {
          "id": "cnh.format#[\"abc\",{\"pad\":true}]",
          "args": [
            "abc",
            {
              "pad": true
            }
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: should return an empty string for a value without digits even when padding"
        },
        {
          "id": "cnh.format#[\"02650306461\"]",
          "args": [
            "02650306461"
          ],
          "expect": {
            "returns": "026503064-61"
          },
          "note": "JavaScript docs example"
        },
        {
          "id": "cnh.format#[\"2650306461\",{\"pad\":true}]",
          "args": [
            "2650306461",
            {
              "pad": true
            }
          ],
          "expect": {
            "returns": "026503064-61"
          },
          "note": "JavaScript docs example"
        }
      ]
    },
    {
      "id": "cnh.generate",
      "level": "extended",
      "summary": "Generates a valid random CNH.",
      "description": "Generates a valid random CNH number: 11 digits, unformatted, accepted by `cnh.isValid`.\n\n- The 9 base digits are never all the same.",
      "params": [],
      "returns": "string",
      "cases": [
        {
          "id": "cnh.generate#generated-is-valid",
          "args": [],
          "expect": {
            "satisfies": "cnh.isValid"
          },
          "repeat": 5,
          "note": "every generated value must pass the lib's own validator"
        }
      ]
    },
    {
      "id": "cnh.isValid",
      "level": "core",
      "summary": "Checks whether a CNH is valid.",
      "description": "Validates a CNH registry number: 9 digits plus 2 check digits (Resolução CONTRAN nº 886/2021, art. 4º).\n\n- The function ignores whitespace, dots, hyphens and slashes, in any number and position (`000000001/19` and `000000001-19` are read as `00000000119`). Any other character makes the value invalid. Until 2.4.0 a slash made the value invalid.\n- Rejects a value whose 11 digits are all the same.\n- Exactly 11 digits are required once the ignored characters are removed.\n- Only a string is read. Any other type returns `false`.\n- No official text publishes the check-digit weights. Resolução CONTRAN nº 886/2021, art. 4º, and Resolução CONTRAN nº 1.020/2025, art. 10, give only the layout (9 characters and 2 check digits). The algorithm follows a community reference.\n- Pending decision (findings §1): the reference (JS) keeps a remainder of 1 in the first check digit as `1`, as real registry numbers do (art. 4º § 1º says `0`). Python and Erlang use a different (2022) algorithm.",
      "params": [
        {
          "name": "value",
          "type": "string"
        }
      ],
      "returns": "boolean",
      "cases": [
        {
          "id": "cnh.isValid#[\"73918433737\"]",
          "args": [
            "73918433737"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"73918433738\"]",
          "args": [
            "73918433738"
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"00000000000\"]",
          "args": [
            "00000000000"
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"55677801844\"]",
          "args": [
            "55677801844"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"55677801845\"]",
          "args": [
            "55677801845"
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"02578228759\"]",
          "args": [
            "02578228759"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"02578228750\"]",
          "args": [
            "02578228750"
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"739184337-37\"]",
          "args": [
            "739184337-37"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"556778018-44\"]",
          "args": [
            "556778018-44"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"025782287-59\"]",
          "args": [
            "025782287-59"
          ],
          "expect": {
            "returns": true
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"\"]",
          "args": [
            ""
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"   \"]",
          "args": [
            "   "
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#[\"abc\"]",
          "args": [
            "abc"
          ],
          "expect": {
            "returns": false
          },
          "note": "consensus of 5 libs (go, javascript, python, ruby, rust)"
        },
        {
          "id": "cnh.isValid#valid-sample",
          "args": [
            "75206264506"
          ],
          "expect": {
            "returns": true
          },
          "note": "valid CNH for go, javascript, ruby and rust; python implements the 2022 CNH algorithm and rejects it: confirm which rule the contract follows (docs/findings.md)"
        },
        {
          "id": "cnh.isValid#[\"00000000119\"]",
          "args": [
            "00000000119"
          ],
          "expect": {
            "returns": true
          },
          "note": "JavaScript's own test: should return true for valid CNH"
        },
        {
          "id": "cnh.isValid#[\"000000001-19\"]",
          "args": [
            "000000001-19"
          ],
          "expect": {
            "returns": true
          },
          "note": "JavaScript's own test: should return true for valid CNH"
        },
        {
          "id": "cnh.isValid#[\"00000009309\"]",
          "args": [
            "00000009309"
          ],
          "expect": {
            "returns": true
          },
          "note": "JavaScript's own test: should return true for a CNH that hits the secondVerifier<0 branch"
        },
        {
          "id": "cnh.isValid#[\"12345678901\"]",
          "args": [
            "12345678901"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false for invalid CNH"
        },
        {
          "id": "cnh.isValid#[\"11111111111\"]",
          "args": [
            "11111111111"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false for invalid CNH"
        },
        {
          "id": "cnh.isValid#[\"00000000129\"]",
          "args": [
            "00000000129"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false when the first verifier digit does not match"
        },
        {
          "id": "cnh.isValid#[\"0000000011900\"]",
          "args": [
            "0000000011900"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false when it sanitizes to more than 11 digits, even if the first 11 match a valid CNH"
        },
        {
          "id": "cnh.isValid#[\"ab00000000119\"]",
          "args": [
            "ab00000000119"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false when a letter is attached to the digits"
        },
        {
          "id": "cnh.isValid#[\"00000000119ab\"]",
          "args": [
            "00000000119ab"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should return false when a letter is attached to the digits"
        },
        {
          "id": "cnh.isValid#[\"000000001/19\"]",
          "args": [
            "000000001/19"
          ],
          "expect": {
            "returns": true
          },
          "note": "JavaScript's own test: should accept a slash among the mask characters, as isValidCpf does, and reject any other character (changed in 2.5.0 (#615): a slash is a mask character)"
        },
        {
          "id": "cnh.isValid#[\"000000001_19\"]",
          "args": [
            "000000001_19"
          ],
          "expect": {
            "returns": false
          },
          "note": "JavaScript's own test: should accept a slash among the mask characters, as isValidCpf does, and reject any other character"
        },
        {
          "id": "cnh.isValid#[\"0000.0000 1/19\"]",
          "args": [
            "0000.0000 1/19"
          ],
          "expect": {
            "returns": true
          },
          "note": "mask characters (whitespace, dot, hyphen, slash) are ignored in any position"
        }
      ]
    },
    {
      "id": "cnh.parse",
      "level": "extended",
      "summary": "Removes the formatting characters of a CNH and returns only the digits.",
      "description": "Removes CNH formatting and keeps only digits, capped at 11 digits (the digits after the 11th are dropped).\n\n- A number is read only when it is a safe non-negative integer. A negative, fractional, non-finite or unsafe number returns an empty string (2.4.0 read the digits of any number, sign and decimal point dropped).\n- A value with no digits returns an empty string.",
      "params": [
        {
          "name": "value",
          "type": "string | number"
        }
      ],
      "returns": "string",
      "cases": [
        {
          "id": "cnh.parse#masked",
          "args": [
            "000000001-19"
          ],
          "expect": {
            "returns": "00000000119"
          }
        },
        {
          "id": "cnh.parse#unmasked",
          "args": [
            "00000000119"
          ],
          "expect": {
            "returns": "00000000119"
          }
        },
        {
          "id": "cnh.parse#strips-non-digits",
          "args": [
            "000.abc000001-19"
          ],
          "expect": {
            "returns": "00000000119"
          }
        },
        {
          "id": "cnh.parse#empty",
          "args": [
            ""
          ],
          "expect": {
            "returns": ""
          }
        },
        {
          "id": "cnh.parse#caps-length",
          "args": [
            "00000000119123"
          ],
          "expect": {
            "returns": "00000000119"
          },
          "note": "reference truncates to the 11 characters of the document; asserted by the reference (JS) unit tests"
        },
        {
          "id": "cnh.parse#[-1]",
          "args": [
            -1
          ],
          "expect": {
            "returns": ""
          },
          "note": "changed in 2.5.0 (#593): a negative number gives an empty string. JavaScript's own test: when it is a negative, fractional or unsafe number"
        },
        {
          "id": "cnh.parse#[1.5]",
          "args": [
            1.5
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: when it is a negative, fractional or unsafe number"
        },
        {
          "id": "cnh.parse#[9007199254740992]",
          "args": [
            9007199254740992
          ],
          "expect": {
            "returns": ""
          },
          "note": "JavaScript's own test: when it is a negative, fractional or unsafe number (2^53)"
        },
        {
          "id": "cnh.parse#[119]",
          "args": [
            119
          ],
          "expect": {
            "returns": "119"
          },
          "note": "a safe non-negative integer is read as before"
        }
      ]
    }
  ]
}
