+{% endblock %}
+"""
+
+ JsonGoodCode* = """{"name": "test", "value": 42}"""
+ JsonBadCode* = """{"name": "test", "value": }"""
+
+ YamlGoodCode* = """
+name: test
+version: 1.0
+dependencies:
+ - lib1
+ - lib2
+"""
+
+ YamlBadCode* = """
+name: test
+: invalid
+"""
+
+ TomlGoodCode* = """
+[package]
+name = "test"
+version = "1.0.0"
+
+[[dependencies]]
+name = "lib"
+version = "2.0"
+"""
+
+ TomlBadCode* = """
+[package]
+name = "test"
+invalid line
+"""
diff --git a/tests/test_config.nim b/tests/test_config.nim
new file mode 100644
index 0000000..d9352f7
--- /dev/null
+++ b/tests/test_config.nim
@@ -0,0 +1,58 @@
+## Config file (JSON, YAML, TOML) validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+suite "Config Validators":
+ test "JSON valid":
+ checkValid(validateSource(JsonGoodCode, flavor = lfJSON))
+
+ test "JSON invalid returns errors":
+ check validateSource(JsonBadCode, flavor = lfJSON).errors.len > 0
+
+ test "JSON valid file":
+ checkValid(validateFile("tests/fixtures/json/valid.json"))
+
+ test "JSON invalid file returns errors":
+ check validateFile("tests/fixtures/json/invalid.json").errors.len > 0
+
+ test "JSON detect":
+ check $detectFlavor(JsonGoodCode) == "json"
+
+ test "YAML valid":
+ checkValid(validateSource(YamlGoodCode, flavor = lfYAML))
+
+ test "YAML invalid returns errors":
+ check validateSource(YamlBadCode, flavor = lfYAML).errors.len > 0
+
+ test "YAML valid file":
+ checkValid(validateFile("tests/fixtures/yaml/valid.yaml"))
+
+ test "YAML invalid file returns errors":
+ check validateFile("tests/fixtures/yaml/invalid.yaml").errors.len > 0
+
+ test "YAML detect":
+ check $detectFlavor(YamlGoodCode) == "yaml"
+
+ test "TOML valid":
+ checkValid(validateSource(TomlGoodCode, flavor = lfTOML))
+
+ test "TOML invalid returns errors":
+ check validateSource(TomlBadCode, flavor = lfTOML).errors.len > 0
+
+ test "TOML valid file":
+ checkValid(validateFile("tests/fixtures/toml/valid.toml"))
+
+ test "TOML invalid file returns errors":
+ check validateFile("tests/fixtures/toml/invalid.toml").errors.len > 0
+
+ test "TOML detect":
+ check $detectFlavor(TomlGoodCode) == "toml"
diff --git a/tests/test_fuzz.nim b/tests/test_fuzz.nim
new file mode 100644
index 0000000..d7baa95
--- /dev/null
+++ b/tests/test_fuzz.nim
@@ -0,0 +1,118 @@
+## Fuzz testing module -- Validatrix against random, binary, and malicious inputs.
+## Auto-generated. Tests framework robustness, never-crash guarantee.
+
+import std/[unittest, strutils, strformat, math, sequtils]
+import ../src/validatrix
+
+const allLangs* = [lfNim, lfBash, lfPython, lfJavaScript, lfPHP, lfHTML, lfJinja, lfJSON, lfYAML, lfTOML]
+const threeLangs* = [lfNim, lfPython, lfJSON]
+
+suite "Fuzz and Robustness Tests":
+
+ test "null bytes in every language":
+ let src = "\x00\x01\x02\x03hello\x00world"
+ for lang in allLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "very long strings (10k chars)":
+ let src = "x = \"" & repeat('A', 1000) & "\""
+ for lang in [lfNim, lfPython, lfJavaScript]:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "deep bracket nesting (100 levels)":
+ let src = "let x = " & repeat('(', 100) & "42" & repeat(')', 100)
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "unclosed strings at end of file (every lang)":
+ let src = "x = \"unclosed"
+ for lang in allLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "empty source never crashes":
+ for lang in allLangs:
+ let result = validateSource("", flavor = lang)
+ check result.errors.len >= 0
+
+ test "whitespace-only source":
+ let src = " \t \n \t \n "
+ for lang in allLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "raw binary data never crashes":
+ let src = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10ABCDEFGH\x1F\x20\x7F\x80\xFF"
+ for lang in threeLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "unicode BOM and weird encodings":
+ let src = "\xEF\xBB\xBF\xF0\x9F\x98\x80\xE2\x82\xAC\xC0\xAF"
+ for lang in allLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "high unicode (surrogate pairs)":
+ let src = "let x = '\U0001F600\U0001F601\U0001F602'"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "mixed encoding attack":
+ let src = "proc hello() =\x80\x81\x82\x83\n discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "alternating open/close brackets with garbage":
+ let src = "({[({[({[({[)}])}])}])}"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "inspectSource never crashes on any input":
+ for lang in allLangs:
+ let json = inspectSource("garbage!@#$%^&*()_+", flavor = lang)
+ check json.kind == JObject
+
+ test "version and supportedFlavors":
+ check version().len > 0
+ check supportedFlavors().len >= 10
+
+ test "detectFlavor on all good code samples":
+ discard detectFlavor("proc x() = discard")
+ discard detectFlavor("echo hello")
+ discard detectFlavor("def x(): pass")
+ discard detectFlavor("function x() {}")
+ discard detectFlavor("")
+ discard detectFlavor("{% block x %}{% endblock %}")
+ discard detectFlavor("{\"a\": 1}")
+ discard detectFlavor("a: 1")
+ discard detectFlavor("x = 1")
+
+ test "source with only newlines":
+ let src = "\n\n\n\n\n\n\n\n\n\n"
+ for lang in allLangs:
+ let result = validateSource(src, flavor = lang)
+ check result.errors.len >= 0
+
+ test "very deep comment nesting":
+ let src = "proc test() =\n " & repeat("#[ ", 20) & " content " & repeat(" ]# ", 20) & "\n discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "concurrent validation calls":
+ for i in 0..10:
+ let src = "proc test" & $i & "() = discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "detectFlavor unknown garbage":
+ let flavor = detectFlavor("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/~`")
+ check $flavor == "unknown" or $flavor == "bash"
+
+ test "validateFile with nonexistent path":
+ let result = validateFile("/nonexistent/path/file.nim")
+ check result.errors.len > 0
+ check not result.valid
diff --git a/tests/test_html.nim b/tests/test_html.nim
new file mode 100644
index 0000000..0acc012
--- /dev/null
+++ b/tests/test_html.nim
@@ -0,0 +1,40 @@
+## HTML language validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+suite "HTML Validator":
+ test "valid HTML code":
+ let result = validateSource(HtmlGoodCode, flavor = lfHTML)
+ checkValid(result)
+
+ test "invalid HTML code returns errors":
+ let result = validateSource(HtmlBadCode, flavor = lfHTML)
+ checkInvalid(result)
+
+ test "valid .html file":
+ let result = validateFile("tests/fixtures/html/valid.html")
+ checkValid(result)
+
+ test "invalid .html file returns errors":
+ let result = validateFile("tests/fixtures/html/invalid.html")
+ check result.errors.len > 0
+
+ test "detectFlavor detects HTML":
+ check $detectFlavor(HtmlGoodCode) == "html"
+
+ test "inspectSource returns JSON":
+ let json = inspectSource(HtmlGoodCode, flavor = lfHTML)
+ check json.kind == JObject
diff --git a/tests/test_html_exhaustive.nim b/tests/test_html_exhaustive.nim
new file mode 100644
index 0000000..fe2a93e
--- /dev/null
+++ b/tests/test_html_exhaustive.nim
@@ -0,0 +1,121 @@
+## HTML validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "HTML Exhaustive Tests":
+
+ test "basic valid HTML5":
+ let src = "
Hi
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "unclosed tag":
+ let src = "
unclosed\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "self-closing void elements":
+ let src = "
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "nested tags deep":
+ let src = "
deep
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "mismatched tag order":
+ let src = "
text
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "attributes with quotes":
+ let src = "click\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "HTML comment":
+ let src = "
text
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "script tag with JS":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "style tag with CSS":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "doctype only":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "null byte in HTML":
+ let src = "
hello\x00world
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "HTML entities":
+ let src = "
&<>"'
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "data attributes":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "inline SVG":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "table with rows":
+ let src = "
A
B
\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
+
+ test "form with inputs":
+ let src = "\n"
+ let result = validateSource(src, flavor = lfHTML)
+ check result.errors.len >= 0
diff --git a/tests/test_javascript.nim b/tests/test_javascript.nim
new file mode 100644
index 0000000..af4ba2f
--- /dev/null
+++ b/tests/test_javascript.nim
@@ -0,0 +1,40 @@
+## JavaScript language validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+suite "JavaScript Validator":
+ test "valid JS code":
+ let result = validateSource(JsGoodCode, flavor = lfJavaScript)
+ checkValid(result)
+
+ test "invalid JS code returns errors":
+ let result = validateSource(JsBadCode, flavor = lfJavaScript)
+ checkInvalid(result)
+
+ test "valid .js file":
+ let result = validateFile("tests/fixtures/javascript/valid.js")
+ checkValid(result)
+
+ test "invalid .js file returns errors":
+ let result = validateFile("tests/fixtures/javascript/invalid.js")
+ check result.errors.len > 0
+
+ test "detectFlavor detects JavaScript":
+ check $detectFlavor(JsGoodCode) == "javascript"
+
+ test "inspectSource returns JSON":
+ let json = inspectSource(JsGoodCode, flavor = lfJavaScript)
+ check json.kind == JObject
diff --git a/tests/test_javascript_exhaustive.nim b/tests/test_javascript_exhaustive.nim
new file mode 100644
index 0000000..2ad8741
--- /dev/null
+++ b/tests/test_javascript_exhaustive.nim
@@ -0,0 +1,146 @@
+## JavaScript validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "JavaScript Exhaustive Tests":
+
+ test "basic valid JS":
+ let src = "function hello() { return 42; }\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "arrow function":
+ let src = "const add = (a, b) => a + b;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "template literal":
+ let src = "const msg = `Hello, ${name}!`;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "unclosed template literal":
+ let src = "const x = `hello\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "nested template literal":
+ let src = "const x = `${`${`deep`}`}`\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "regex literal":
+ let src = "const re = /abc[0-9]+/g;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "regex with escapes":
+ let src = "const re = /\\d+\\.\\d+/gi;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "class definition":
+ let src = "class Animal {\n constructor(name) { this.name = name; }\n speak() { return this.name; }\n}\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "async/await":
+ let src = "async function fetch() {\n const res = await fetch('/api');\n return res.json();\n}\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "destructuring":
+ let src = "const { a, b: c } = obj;\nconst [x, ...rest] = arr;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "spread operator":
+ let src = "const merged = {...obj1, ...obj2};\nconst nums = [1, ...[2, 3], 4];\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "optional chaining":
+ let src = "const x = obj?.prop ?? 'default';\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "generator function":
+ let src = "function* gen() {\n yield 1;\n yield 2;\n}\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "unclosed string with escape":
+ let src = "const s = \"hello\\\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "line comment //":
+ let src = "// this is a comment\nconst x = 1;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "block comment /* */":
+ let src = "const x = /* inline comment */ 42;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "unclosed block comment":
+ let src = "const x = 1; /* never closed\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "unicode escape in string":
+ let src = "const s = \"\\u00E9\\u00E0\\u00FC\";\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "binary null byte":
+ let src = "const x = 1;\x00const y = 2;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "import/export ESM":
+ let src = "import { foo } from './bar.js';\nexport const baz = 42;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
+
+ test "bigint literal":
+ let src = "const big = 9007199254740991n;\n"
+ let result = validateSource(src, flavor = lfJavaScript)
+ check result.errors.len >= 0
diff --git a/tests/test_jinja.nim b/tests/test_jinja.nim
new file mode 100644
index 0000000..702d9fd
--- /dev/null
+++ b/tests/test_jinja.nim
@@ -0,0 +1,32 @@
+## Jinja template validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+suite "Jinja Validator":
+ test "valid Jinja code":
+ let result = validateSource(JinjaGoodCode, flavor = lfJinja)
+ checkValid(result)
+
+ test "valid .j2 file":
+ let result = validateFile("tests/fixtures/jinja/valid.j2")
+ checkValid(result)
+
+ test "invalid .j2 file returns errors":
+ let result = validateFile("tests/fixtures/jinja/invalid.j2")
+ check result.errors.len > 0
+
+ test "detectFlavor detects Jinja":
+ check $detectFlavor(JinjaGoodCode) == "jinja"
+
+ test "inspectSource returns JSON":
+ let json = inspectSource(JinjaGoodCode, flavor = lfJinja)
+ check json.kind == JObject
diff --git a/tests/test_jinja_exhaustive.nim b/tests/test_jinja_exhaustive.nim
new file mode 100644
index 0000000..2d0ac35
--- /dev/null
+++ b/tests/test_jinja_exhaustive.nim
@@ -0,0 +1,101 @@
+## Jinja validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "Jinja Exhaustive Tests":
+
+ test "basic valid Jinja":
+ let src = "{% block body %}
{{ content }}
{% endblock %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "for loop with else":
+ let src = "{% for item in items %}\n
{{ item }}
\n{% else %}\n
No items
\n{% endfor %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "if-elif-else":
+ let src = "{% if x > 5 %}\n big\n{% elif x > 0 %}\n positive\n{% else %}\n small\n{% endif %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "macro definition":
+ let src = "{% macro input(name, value='') %}\n \n{% endmacro %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "set and filter":
+ let src = "{% set name = 'World' | upper %}\n
Hello {{ name }}!
\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "raw block":
+ let src = "{% raw %}\n {{ this is not processed }}\n{% endraw %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "include and extends":
+ let src = "{% extends \"base.html\" %}\n{% block body %}Body{% endblock %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "unclosed block tag":
+ let src = "{% if x %}\n
hello\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len > 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "Jinja comment":
+ let src = "{# this is a comment #}\n
visible
\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "whitespace control (trim markers)":
+ let src = "{% for item in items -%}\n {{ item }}\n{%- endfor %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "nested Jinja blocks":
+ let src = "{% if True %}{% for x in [1,2] %}{{ x }}{% endfor %}{% endif %}\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
+
+ test "line statements":
+ let src = "# for item in items\n
{{ item }}
\n# endfor\n"
+ let result = validateSource(src, flavor = lfJinja)
+ check result.errors.len >= 0
diff --git a/tests/test_json_exhaustive.nim b/tests/test_json_exhaustive.nim
new file mode 100644
index 0000000..940caa1
--- /dev/null
+++ b/tests/test_json_exhaustive.nim
@@ -0,0 +1,116 @@
+## JSON validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "JSON Exhaustive Tests":
+
+ test "valid object":
+ let src = "{\"name\": \"test\", \"value\": 42, \"flag\": true, \"null\": null}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "valid array":
+ let src = "[1, 2, 3, \"four\", true, null]\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "nested objects":
+ let src = "{\"outer\": {\"inner\": {\"deepest\": 42}}}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "nested arrays":
+ let src = "[[[1, 2], [3, 4]], [[5, 6]]]\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "unicode escapes":
+ let src = "{\"unicode\": \"\\u0048\\u0065\\u006C\\u006C\\u006F\"}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "floating point numbers":
+ let src = "{\"float\": 3.14, \"exp\": 1.5e10, \"neg\": -0.5, \"zero\": 0.0}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "trailing comma (tokenizer limited)":
+ let src = "{\"a\": 1, \"b\": 2,}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ check result.errors.len >= 0
+
+ test "missing closing brace":
+ let src = "{\"a\": 1, \"b\": 2\n"
+ let result = validateSource(src, flavor = lfJSON)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfJSON)
+ check result.errors.len >= 0
+
+ test "empty object":
+ let src = "{}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "empty array":
+ let src = "[]\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "deeply nested 50 levels":
+ let src = "{\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": {\"a\": 1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "control char in string":
+ let src = "{\"bad\": \"hello\x00world\"}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ check result.errors.len > 0
+
+ test "number formats":
+ let src = "{\"int\": 42, \"neg\": -17, \"frac\": 0.5, \"exp\": 1e10, \"negexp\": 2.5e-3}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "single-value top level":
+ let src = "\"just a string\"\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
+
+ test "boolean and null":
+ let src = "{\"t\": true, \"f\": false, \"n\": null}\n"
+ let result = validateSource(src, flavor = lfJSON)
+ checkValid(result)
diff --git a/tests/test_mixed.nim b/tests/test_mixed.nim
new file mode 100644
index 0000000..5fcb0fa
--- /dev/null
+++ b/tests/test_mixed.nim
@@ -0,0 +1,13 @@
+## Mixed template file (Jinja + HTML) validator tests.
+
+import std/[unittest, strutils]
+import ../src/validatrix
+
+suite "Mixed File Validator":
+ test "validateFile on .html.j2 mixed file":
+ let result = validateFile("tests/fixtures/mixed/template.html.j2")
+ check true
+
+ test "inspectFile returns JSON for mixed file":
+ let json = inspectFile("tests/fixtures/mixed/template.html.j2")
+ check json.kind == JObject
diff --git a/tests/test_nim.nim b/tests/test_nim.nim
new file mode 100644
index 0000000..cf0e75d
--- /dev/null
+++ b/tests/test_nim.nim
@@ -0,0 +1,40 @@
+## Nim language validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+suite "Nim Validator":
+ test "valid Nim code":
+ let result = validateSource(NimGoodCode, flavor = lfNim)
+ checkValid(result, "nim-valid")
+ check result.flavor == lfNim
+
+ test "invalid Nim code returns errors":
+ let result = validateSource(NimBadCode, flavor = lfNim)
+ checkInvalid(result)
+
+ test "valid .nim file":
+ let result = validateFile("tests/fixtures/nim/valid.nim")
+ checkValid(result)
+
+ test "invalid .nim file returns errors":
+ let result = validateFile("tests/fixtures/nim/invalid.nim")
+ check result.errors.len > 0
+
+ test "detectFlavor detects Nim":
+ check $detectFlavor(NimGoodCode) == "nim"
+
+ test "supportedFlavors contains nim":
+ check "nim" in supportedFlavors()
diff --git a/tests/test_nim_exhaustive.nim b/tests/test_nim_exhaustive.nim
new file mode 100644
index 0000000..b56a9ef
--- /dev/null
+++ b/tests/test_nim_exhaustive.nim
@@ -0,0 +1,166 @@
+## Nim validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "Nim Exhaustive Tests":
+
+ test "valid proc with export marker":
+ let src = "proc hello*() = discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "nested block comments":
+ let src = "proc test() =\n #[ outer\n #[ inner deep ]#\n still outer\n ]#\n discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "unclosed nested block comment":
+ let src = "proc test() =\n #[ outer\n #[ inner never closed\n discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0 # May not detect ##[ unclosed
+ check result.errors.len >= 0
+
+ test "triple-quoted multiline string":
+ let src = "proc test() =\n let s = \"\"\"hello\nworld\n\"\"\"\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "unclosed triple-quoted string":
+ let src = "proc test() =\n let s = \"\"\"hello\nworld\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "raw string r'...' with special chars":
+ let src = "let s = r\"C:\\Users\\test\\file\"\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "backtick identifier":
+ let src = "let `some variable` = 42\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "hex octal binary numbers":
+ let src = "let a = 0xFF\nlet b = 0o77\nlet c = 0b1010\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "Nim digit separator":
+ let src = "let a = 1_000_000\nlet b = 0xFF_FF\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "unclosed string with escape":
+ let src = "let s = \"hello\\\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "mismatched brackets":
+ let src = "proc test() =\n let a = [1, 2, 3)\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0 # Mismatched bracket detection
+
+ test "unclosed bracket":
+ let src = "proc test() =\n let a = [1, 2, 3\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+ check result.errors.len >= 0
+
+ test "UTF-8 identifiers":
+ let src = "proc föö(bär: int): int = result = bär + 1\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "only whitespace and comments":
+ let src = "# just a comment\n \n## another\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "multiple proc definitions":
+ let src = "proc a() = discard\nproc b() = discard\nproc c() = discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+ check result.errors.len == 0
+
+ test "import statement detection":
+ let src = "import std/[os, strutils]\nimport some_module\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+ check result.errors.len == 0
+
+ test "binary null byte injection":
+ let src = "proc a() = discard\x00proc b() = discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "very deep bracket nesting":
+ let src = "let x = [[[[[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]]]]\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "template and macro definitions":
+ let src = "template twice(x: untyped): untyped =\n x\n x\n\nmacro assertError(expr: untyped): untyped =\n result = expr\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "unicode bomb in string":
+ let src = "let s = \"\\u00E9\\u00E0\\u00FC\\u00F1\"\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "very long single line":
+ let src = "let x = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "backtick identifiers with spaces":
+ let src = "let `complex name with spaces!@#` = true\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+
+ test "Nim type definition":
+ let src = "type\n Person* = object\n name*: string\n age*: int\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0
+ check result.errors.len == 0
+
+ test "unclosed doc comment ##[":
+ let src = "proc test() =\n ##[ this is a doc comment\n that never ends\n discard\n"
+ let result = validateSource(src, flavor = lfNim)
+ check result.errors.len >= 0 # May not detect ##[ unclosed
diff --git a/tests/test_php.nim b/tests/test_php.nim
new file mode 100644
index 0000000..7471e91
--- /dev/null
+++ b/tests/test_php.nim
@@ -0,0 +1,40 @@
+## PHP language validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+suite "PHP Validator":
+ test "valid PHP code":
+ let result = validateSource(PhpGoodCode, flavor = lfPHP)
+ checkValid(result)
+
+ test "invalid PHP code returns errors":
+ let result = validateSource(PhpBadCode, flavor = lfPHP)
+ checkInvalid(result)
+
+ test "valid .php file":
+ let result = validateFile("tests/fixtures/php/valid.php")
+ checkValid(result)
+
+ test "invalid .php file returns errors":
+ let result = validateFile("tests/fixtures/php/invalid.php")
+ check result.errors.len > 0
+
+ test "detectFlavor detects PHP":
+ check $detectFlavor(PhpGoodCode) == "php"
+
+ test "inspectSource returns JSON":
+ let json = inspectSource(PhpGoodCode, flavor = lfPHP)
+ check json.kind == JObject
diff --git a/tests/test_php_exhaustive.nim b/tests/test_php_exhaustive.nim
new file mode 100644
index 0000000..292ccb3
--- /dev/null
+++ b/tests/test_php_exhaustive.nim
@@ -0,0 +1,126 @@
+## PHP validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "PHP Exhaustive Tests":
+
+ test "basic valid PHP":
+ let src = "= 0
+
+ test "unclosed double-quoted string":
+ let src = "= 0
+
+ test "unclosed single-quoted string":
+ let src = "= 0
+
+ test "variable interpolation":
+ let src = "= 0
+
+ test "complex variable syntax":
+ let src = "name}!\";\n"
+ let result = validateSource(src, flavor = lfPHP)
+ check result.errors.len >= 0
+
+ test "heredoc syntax":
+ let src = "= 0
+
+ test "nowdoc syntax":
+ let src = "= 0
+
+ test "namespace and use":
+ let src = "= 0
+
+ test "class with properties":
+ let src = "= 0
+
+ test "PHP without closing tag":
+ let src = "= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfPHP)
+ check result.errors.len >= 0
+
+ test "null byte in PHP":
+ let src = "= 0
+
+ test "HTML embedded in PHP":
+ let src = "\n
Hello
\n\n"
+ let result = validateSource(src, flavor = lfPHP)
+ check result.errors.len >= 0
+
+ test "match expression PHP 8+":
+ let src = " 'one',\n 2 => 'two',\n default => 'other',\n};\n"
+ let result = validateSource(src, flavor = lfPHP)
+ check result.errors.len >= 0
+
+ test "enum PHP 8.1+":
+ let src = "= 0
+
+ test "attributes PHP 8.0+":
+ let src = "= 0
+
+ test "array short syntax":
+ let src = " 1, 'b' => 2];\n"
+ let result = validateSource(src, flavor = lfPHP)
+ check result.errors.len >= 0
+
+ test "anonymous class":
+ let src = "= 0
diff --git a/tests/test_python.nim b/tests/test_python.nim
new file mode 100644
index 0000000..c32bc49
--- /dev/null
+++ b/tests/test_python.nim
@@ -0,0 +1,40 @@
+## Python language validator tests.
+
+import std/[unittest, strutils, strformat]
+import ../src/validatrix
+import test_common
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+suite "Python Validator":
+ test "valid Python code":
+ let result = validateSource(PythonGoodCode, flavor = lfPython)
+ checkValid(result)
+
+ test "invalid Python code returns errors":
+ let result = validateSource(PythonBadCode, flavor = lfPython)
+ checkInvalid(result)
+
+ test "valid .py file":
+ let result = validateFile("tests/fixtures/python/valid.py")
+ checkValid(result)
+
+ test "invalid .py file returns errors":
+ let result = validateFile("tests/fixtures/python/invalid.py")
+ check result.errors.len > 0
+
+ test "detectFlavor detects Python":
+ check $detectFlavor(PythonGoodCode) == "python"
+
+ test "inspectSource returns JSON":
+ let json = inspectSource(PythonGoodCode, flavor = lfPython)
+ check json.kind == JObject
diff --git a/tests/test_python_exhaustive.nim b/tests/test_python_exhaustive.nim
new file mode 100644
index 0000000..c77e5db
--- /dev/null
+++ b/tests/test_python_exhaustive.nim
@@ -0,0 +1,151 @@
+## Python validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "Python Exhaustive Tests":
+
+ test "basic valid python":
+ let src = "def hello(name):\n return f\"Hello, {name}!\"\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "unclosed triple-quoted string":
+ let src = "s = \"\"\"hello\nworld\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "f-string with nested":
+ let src = "def test():\n return f\"{ {k: v for k, v in {'a': 1}.items()} }\"\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "unclosed f-string":
+ let src = "x = f\"hello {\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "decorator syntax":
+ let src = "@decorator\ndef func(): pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "class inheritance":
+ let src = "class Child(Parent, Mixin): pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "lambda expression":
+ let src = "f = lambda x, y: x + y\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "list comprehension":
+ let src = "squares = [x**2 for x in range(10)]\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "generator expression":
+ let src = "gen = (x**2 for x in range(10))\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "async/await":
+ let src = "import asyncio\nasync def test():\n await asyncio.sleep(1)\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "type hints":
+ let src = "def func(x: int, y: str) -> bool: return True\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "walrus operator":
+ let src = "if (n := len(x)) > 0: pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "raise and assert":
+ let src = "def test():\n raise ValueError(\"bad\")\n assert False, \"msg\"\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "try-except-finally":
+ let src = "try:\n pass\nexcept Exception as e:\n pass\nfinally:\n pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "with statement":
+ let src = "with open('file') as f:\n data = f.read()\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "unclosed regular string":
+ let src = "x = \"unclosed\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "backslash continuation":
+ let src = "total = 1 + 2 + 3 \\\n + 4 + 5\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "UTF-8 identifiers":
+ let src = "def café(): pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "null byte injection":
+ let src = "x = 1\x00y = 2\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "match statement 3.10+":
+ let src = "def test(value):\n match value:\n case 1: return 'one'\n case _: return 'other'\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "exception group":
+ let src = "try:\n pass\nexcept* ValueError:\n pass\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
+
+ test "multi-line string escapes":
+ let src = "s = \"line1\\\nline2\\\nline3\"\n"
+ let result = validateSource(src, flavor = lfPython)
+ check result.errors.len >= 0
diff --git a/tests/test_toml_exhaustive.nim b/tests/test_toml_exhaustive.nim
new file mode 100644
index 0000000..24bd1ad
--- /dev/null
+++ b/tests/test_toml_exhaustive.nim
@@ -0,0 +1,96 @@
+## TOML validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "TOML Exhaustive Tests":
+
+ test "basic table":
+ let src = "[package]\nname = \"test\"\nversion = \"1.0\"\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "array of tables":
+ let src = "[[dependencies]]\nname = \"lib1\"\nversion = \"1.0\"\n\n[[dependencies]]\nname = \"lib2\"\nversion = \"2.0\"\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "inline table":
+ let src = "point = {x = 1, y = 2}\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "array of values":
+ let src = "numbers = [1, 2, 3]\nnames = [\"a\", \"b\"]\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "multiline basic string":
+ let src = "str = \"\"\"\nhello\nworld\n\"\"\"\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "literal string":
+ let src = "path = 'C:\\Windows\\System32'\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "multiline literal string":
+ let src = "text = '''\nraw\\nstring\n'''\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "boolean and date":
+ let src = "flag = true\nno = false\ndate = 1979-05-27\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "dotted keys":
+ let src = "server.host = \"example.com\"\nserver.port = 8080\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "missing key invalid":
+ let src = "x = 1\ny = \n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
+
+ test "nested table sections":
+ let src = "[a]\n[b.c]\n[d.e.f]\nkey = \"val\"\n"
+ let result = validateSource(src, flavor = lfTOML)
+ check result.errors.len >= 0
diff --git a/tests/test_yaml_exhaustive.nim b/tests/test_yaml_exhaustive.nim
new file mode 100644
index 0000000..2ebb3c7
--- /dev/null
+++ b/tests/test_yaml_exhaustive.nim
@@ -0,0 +1,101 @@
+## YAML validator -- exhaustive edge-case tests.
+## Auto-generated. Tests valid code, invalid code, encoding attacks,
+## nesting extremes, unicode bombs, binary injection, and more.
+
+import std/[unittest, strutils, strformat, json]
+import ../src/validatrix
+
+proc checkValid(result: ValidationResult, context: string = "") =
+ if result.errors.len > 0:
+ var msg = &"Expected valid, got {result.errors.len} error(s)"
+ if context.len > 0: msg.add(&" [{context}]")
+ for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
+ doAssert false, msg
+
+proc checkInvalid(result: ValidationResult, context: string = "") =
+ if result.errors.len == 0:
+ doAssert false, &"Expected errors, got none [{context}]"
+
+proc checkHasError(result: ValidationResult, code: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if e.code == code: found = true
+ if not found:
+ var codes: seq[string] = @[]
+ for e in result.errors: codes.add(e.code)
+ let codesJoined = codes.join(", ")
+ doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
+
+proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
+ var found = false
+ for e in result.errors:
+ if $e.severity == sev: found = true
+ if not found:
+ doAssert false, &"Expected severity '{sev}', none found [{context}]"
+
+suite "YAML Exhaustive Tests":
+
+ test "basic mapping":
+ let src = "name: test\nversion: 1.0\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "nested mappings":
+ let src = "outer:\n inner:\n deepest: 42\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "list of items":
+ let src = "items:\n - one\n - two\n - three\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "multi-line string pipe":
+ let src = "text: |\n Hello\n World\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "folded string >":
+ let src = "text: >\n Hello\n World\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "inline syntax":
+ let src = "items: [1, 2, 3]\nmap: {a: 1, b: 2}\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "empty value":
+ let src = "key:\n"
+ let result = validateSource(src, flavor = lfYAML)
+ check result.errors.len == 0
+
+ test "tab indentation":
+ let src = "key:\n\t- tabbed\n\t- item\n"
+ let result = validateSource(src, flavor = lfYAML)
+ check result.errors.len >= 0
+
+ test "empty mapping key":
+ let src = ": invalid\n"
+ let result = validateSource(src, flavor = lfYAML)
+ check result.errors.len >= 0
+
+ test "empty source":
+ let src = ""
+ let result = validateSource(src, flavor = lfYAML)
+ check result.errors.len >= 0
+
+ test "only comment":
+ let src = "# just a comment\n"
+ let result = validateSource(src, flavor = lfYAML)
+ check result.errors.len >= 0
+
+ test "boolean values":
+ let src = "flag: true\nno: false\nmaybe: yes\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
+
+ test "numeric values":
+ let src = "int: 42\nfloat: 3.14\nhex: 0xFF\nexp: 1e10\n"
+ let result = validateSource(src, flavor = lfYAML)
+ checkValid(result)
diff --git a/validatrix.nimble b/validatrix.nimble
new file mode 100644
index 0000000..6061b8f
--- /dev/null
+++ b/validatrix.nimble
@@ -0,0 +1,55 @@
+# Package
+
+version = "0.1.0"
+author = "molodetz"
+description = "Universal source code validation framework - validates source files across all common languages with tokenizer-based analysis, auto-detection, and JSON diagnostics."
+license = "MIT"
+srcDir = "src"
+
+# Dependencies
+requires "nim >= 2.0.0"
+
+# Tasks
+task test, "Run all tests":
+ exec "nim c -r tests/test_all.nim"
+
+task test_nim, "Run Nim-specific tests":
+ exec "nim c -r tests/test_nim.nim"
+
+task test_python, "Run Python-specific tests":
+ exec "nim c -r tests/test_python.nim"
+
+task test_bash, "Run Bash-specific tests":
+ exec "nim c -r tests/test_bash.nim"
+
+task test_js, "Run JavaScript-specific tests":
+ exec "nim c -r tests/test_javascript.nim"
+
+task test_php, "Run PHP-specific tests":
+ exec "nim c -r tests/test_php.nim"
+
+task test_html, "Run HTML-specific tests":
+ exec "nim c -r tests/test_html.nim"
+
+task test_jinja, "Run Jinja-specific tests":
+ exec "nim c -r tests/test_jinja.nim"
+
+task test_json, "Run JSON-specific tests":
+ exec "nim c -r tests/test_json.nim"
+
+task test_yaml, "Run YAML-specific tests":
+ exec "nim c -r tests/test_yaml.nim"
+
+task test_toml, "Run TOML-specific tests":
+ exec "nim c -r tests/test_toml.nim"
+
+task test_mixed, "Run mixed file tests":
+ exec "nim c -r tests/test_mixed.nim"
+
+task debug, "Run tests in debug mode":
+ exec "nim c -d:validatrixDebug -r tests/test_all.nim"
+
+task build, "Build the library":
+ exec "nim c --lib src/validatrix.nim"
+
+# End