nip/tests/test_variant_parser.nim

298 lines
9.3 KiB
Nim

## test_variant_parser.nim
## Tests for CLI flag parser
## Ensures proper parsing of domain-scoped and legacy flags
import std/[unittest, tables, strutils]
import ../src/nimpak/variant_parser
import ../src/nimpak/variant_types
import ../src/nimpak/variant_validator
suite "Domain-Scoped Flag Parsing Tests":
test "Parse simple domain flag":
let flag = parseDomainFlag("+init=dinit")
check flag.domain == "init"
check flag.name == "dinit"
check flag.enabled == true
check flag.value == "dinit"
test "Parse domain flag with multiple values":
let flag = parseDomainFlag("+security=pie,relro,hardened")
check flag.domain == "security"
check flag.enabled == true
check flag.value == "pie,relro,hardened"
test "Parse disabled domain flag":
let flag = parseDomainFlag("-optimization=debug")
check flag.domain == "optimization"
check flag.enabled == false
test "Parse domain flag without prefix defaults to enabled":
let flag = parseDomainFlag("runtime=ssl")
check flag.domain == "runtime"
check flag.enabled == true
test "Parse domain flag with spaces":
let flag = parseDomainFlag("+ security = pie , relro ")
check flag.domain == "security"
check flag.value.contains("pie")
check flag.value.contains("relro")
test "Invalid domain raises error":
expect(ParseError):
discard parseDomainFlag("+invalid_domain=value")
test "Invalid value raises error":
expect(ParseError):
discard parseDomainFlag("+init=invalid_init_system")
test "Empty flag raises error":
expect(ParseError):
discard parseDomainFlag("")
test "Flag without value raises error":
expect(ParseError):
discard parseDomainFlag("+init=")
suite "Legacy Flag Parsing Tests":
test "Parse legacy enabled flag":
let flag = parseLegacyFlag("+lto")
check flag.name == "lto"
check flag.enabled == true
check flag.domain == "optimization"
test "Parse legacy disabled flag":
let flag = parseLegacyFlag("-systemd")
check flag.name == "systemd"
check flag.enabled == false
check flag.domain == "init"
test "Parse legacy flag without prefix":
let flag = parseLegacyFlag("ssl")
check flag.name == "ssl"
check flag.enabled == true
test "Legacy flag maps to correct domain - init":
let flag = parseLegacyFlag("+dinit")
check flag.domain == "init"
test "Legacy flag maps to correct domain - security":
let flag = parseLegacyFlag("+pie")
check flag.domain == "security"
test "Legacy flag maps to correct domain - graphics":
let flag = parseLegacyFlag("+wayland")
check flag.domain == "graphics"
test "Legacy flag maps to correct domain - audio":
let flag = parseLegacyFlag("+pipewire")
check flag.domain == "audio"
test "Unknown legacy flag maps to runtime":
let flag = parseLegacyFlag("+unknown_flag")
check flag.domain == "runtime"
test "Empty legacy flag raises error":
expect(ParseError):
discard parseLegacyFlag("")
test "Invalid legacy flag raises error":
expect(ParseError):
discard parseLegacyFlag("+")
suite "Auto-Detection Tests":
test "Domain-scoped flag detected correctly":
let flag = parseDomainFlag("+init=dinit")
check flag.domain == "init"
test "Legacy flag auto-detected and parsed":
let flag = parseDomainFlag("+lto")
check flag.domain == "optimization"
check flag.name == "lto"
suite "Multi-Flag Parsing Tests":
test "Parse multiple flags":
let flags = parseFlags(@["+init=dinit", "+security=pie,relro", "+optimization=lto"])
check flags.len == 3
check flags[0].domain == "init"
check flags[1].domain == "security"
check flags[2].domain == "optimization"
test "Parse mixed domain and legacy flags":
let flags = parseFlags(@["+init=dinit", "+lto", "-systemd"])
check flags.len == 3
check flags[0].domain == "init"
check flags[1].domain == "optimization"
check flags[2].domain == "init"
test "Parse flags from string":
let flags = parseFlagsString("+init=dinit +security=pie,relro -optimization=debug")
check flags.len == 3
test "Empty flags list returns empty":
let flags = parseFlags(@[])
check flags.len == 0
test "Flags with empty strings are skipped":
let flags = parseFlags(@["+init=dinit", "", "+security=pie"])
check flags.len == 2
suite "Flag Grouping Tests":
test "Group flags by domain":
let flags = parseFlags(@["+init=dinit", "+security=pie", "+security=relro"])
let grouped = groupFlagsByDomain(flags)
check grouped.hasKey("init")
check grouped.hasKey("security")
check grouped["init"] == @["dinit"]
check "pie" in grouped["security"]
check "relro" in grouped["security"]
test "Disabled flags are excluded from grouping":
let flags = parseFlags(@["+init=dinit", "-security=pie"])
let grouped = groupFlagsByDomain(flags)
check grouped.hasKey("init")
check not grouped.hasKey("security")
test "Multiple values in single flag are split":
let flags = parseFlags(@["+security=pie,relro,hardened"])
let grouped = groupFlagsByDomain(flags)
check grouped["security"].len == 3
check "pie" in grouped["security"]
check "relro" in grouped["security"]
check "hardened" in grouped["security"]
test "Duplicate values are deduplicated":
let flags = parseFlags(@["+security=pie", "+security=pie"])
let grouped = groupFlagsByDomain(flags)
check grouped["security"].len == 1
check grouped["security"] == @["pie"]
suite "Parse and Validate Tests":
test "Valid flags pass validation":
let domains = parseAndValidate(@["+init=dinit", "+security=pie,relro"])
check domains.hasKey("init")
check domains.hasKey("security")
test "Invalid domain fails validation":
expect(ParseError):
discard parseAndValidate(@["+invalid_domain=value"])
test "Invalid value fails validation":
expect(ParseError):
discard parseAndValidate(@["+init=invalid_init"])
test "Conflicting flags fail validation":
expect(VariantError):
discard parseAndValidate(@["+init=dinit", "+init=systemd"])
test "Parse and validate string":
let domains = parseAndValidateString("+init=dinit +security=pie")
check domains.hasKey("init")
check domains.hasKey("security")
suite "Helper Function Tests":
test "isDomainScopedFlag detects domain syntax":
check isDomainScopedFlag("+init=dinit") == true
check isDomainScopedFlag("+lto") == false
test "isLegacyFlag detects legacy syntax":
check isLegacyFlag("+lto") == true
check isLegacyFlag("+init=dinit") == false
test "extractDomainFromFlag extracts domain":
check extractDomainFromFlag("+init=dinit") == "init"
check extractDomainFromFlag("+security=pie,relro") == "security"
check extractDomainFromFlag("+lto") == "optimization"
test "extractValuesFromFlag extracts values":
let values1 = extractValuesFromFlag("+security=pie,relro,hardened")
check values1.len == 3
check "pie" in values1
let values2 = extractValuesFromFlag("+lto")
check values2.len == 1
check values2[0] == "lto"
test "extractValuesFromFlag handles spaces":
let values = extractValuesFromFlag("+security= pie , relro ")
check values.len == 2
check "pie" in values
check "relro" in values
suite "Edge Cases Tests":
test "Flag with trailing comma":
let flag = parseDomainFlag("+security=pie,relro,")
check flag.value.contains("pie")
check flag.value.contains("relro")
test "Flag with leading comma is handled":
# This should work or raise a clear error
try:
let flag = parseDomainFlag("+security=,pie,relro")
check "pie" in flag.value
except ParseError:
check true # Expected behavior
test "Flag with only commas raises error":
expect(ParseError):
discard parseDomainFlag("+security=,,,")
test "Very long flag value":
let longValue = "pie,relro,hardened,fortify,stack-protector"
let flag = parseDomainFlag("+security=" & longValue)
check flag.value == longValue
test "Unicode in flag name is rejected":
expect(ParseError):
discard parseDomainFlag("+init=dînit")
suite "Real-World Usage Tests":
test "Fleet node configuration":
let flags = parseFlagsString("+init=dinit +runtime=ssl,http3,zstd +security=pie,relro,hardened +optimization=lto,march-native +network=ipv6,wireguard")
let domains = groupFlagsByDomain(flags)
check domains.hasKey("init")
check domains.hasKey("runtime")
check domains.hasKey("security")
check domains.hasKey("optimization")
check domains.hasKey("network")
test "Gaming rig configuration":
let flags = parseFlagsString("+init=systemd +graphics=wayland +audio=pipewire +runtime=steam +optimization=lto,march-native")
let domains = groupFlagsByDomain(flags)
check domains["init"] == @["systemd"]
check domains["graphics"] == @["wayland"]
check domains["audio"] == @["pipewire"]
test "Minimal configuration":
let flags = parseFlagsString("+init=dinit")
let domains = groupFlagsByDomain(flags)
check domains.len == 1
check domains["init"] == @["dinit"]
test "Mixed legacy and modern syntax":
let flags = parseFlagsString("+init=dinit +lto +pie -systemd")
let domains = groupFlagsByDomain(flags)
check domains.hasKey("init")
check domains.hasKey("optimization")
check domains.hasKey("security")
when isMainModule:
echo "Running variant parser tests..."