298 lines
9.3 KiB
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..."
|