## 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..."