# tests for the nixos options to jsonschema converter # run these tests via `nix-unit ./test.nix` { lib ? (import { }).lib, slib ? (import ./. { inherit lib; } { }), }: let description = "Test Description"; # Wrap the parseOption function to reduce the surface that needs to be migrated, when '$exportedModuleInfo' changes parseOption = opt: filterSchema (slib.parseOption opt); evalType = type: default: (evalModuleOptions { options.opt = lib.mkOption { inherit type description; default = default; }; }).opt; evalModuleOptions = module: let evaledConfig = lib.evalModules { modules = [ module ]; }; in evaledConfig.options; filterSchema = schema: lib.filterAttrsRecursive (name: _value: name != "$exportedModuleInfo") schema; in { testNoDefaultNoDescription = let evaledConfig = lib.evalModules { modules = [ { options.opt = lib.mkOption { type = lib.types.bool; }; } ]; }; in { expr = parseOption evaledConfig.options.opt; expected = { type = "boolean"; }; }; testDescriptionIsAttrs = let evaledConfig = lib.evalModules { modules = [ { options.opt = lib.mkOption { type = lib.types.bool; description = { _type = "mdDoc"; text = description; }; }; } ]; }; in { expr = parseOption evaledConfig.options.opt; expected = { type = "boolean"; inherit description; }; }; testBool = let default = false; in { expr = parseOption (evalType lib.types.bool default); expected = { type = "boolean"; inherit default description; }; }; testString = let default = "hello"; in { expr = parseOption (evalType lib.types.str default); expected = { type = "string"; inherit default description; }; }; testInteger = let default = 42; in { expr = parseOption (evalType lib.types.int default); expected = { type = "integer"; inherit default description; }; }; testFloat = let default = 42.42; in { expr = parseOption (evalType lib.types.float default); expected = { type = "number"; inherit default description; }; }; testEnum = let default = "foo"; values = [ "foo" "bar" "baz" ]; in { expr = parseOption (evalType (lib.types.enum values) default); expected = { enum = values; inherit default description; }; }; testListOfInt = let default = [ 1 2 3 ]; in { expr = parseOption (evalType (lib.types.listOf lib.types.int) default); expected = { type = "array"; items = { type = "integer"; }; inherit default description; }; }; testListOfUnspecified = let default = [ 1 2 3 ]; in { expr = parseOption (evalType (lib.types.listOf lib.types.unspecified) default); expected = { type = "array"; items = { type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; }; inherit default description; }; }; testAttrs = let default = { foo = 1; bar = 2; baz = 3; }; in { expr = parseOption (evalType (lib.types.attrs) default); expected = { type = "object"; additionalProperties = true; inherit default description; }; }; testAttrsOfInt = let default = { foo = 1; bar = 2; baz = 3; }; in { expr = parseOption (evalType (lib.types.attrsOf lib.types.int) default); expected = { type = "object"; additionalProperties = { type = "integer"; }; inherit default description; }; }; testLazyAttrsOfInt = let default = { foo = 1; bar = 2; baz = 3; }; in { expr = parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default); expected = { type = "object"; additionalProperties = { type = "integer"; }; inherit default description; }; }; testNullOrBool = let default = null; # null is a valid value for this type in { expr = parseOption (evalType (lib.types.nullOr lib.types.bool) default); expected = { oneOf = [ { type = "null"; } { type = "boolean"; "$exportedModuleInfo" = { path = [ "opt" ]; default = null; defaultText = null; required = true; }; } ]; inherit default description; }; }; testNullOrNullOr = let default = null; # null is a valid value for this type in { expr = parseOption (evalType (lib.types.nullOr (lib.types.nullOr lib.types.bool)) default); expected = { oneOf = [ { type = "null"; } { "$exportedModuleInfo" = { default = null; defaultText = null; path = [ "opt" ]; required = true; }; oneOf = [ { type = "null"; } { "$exportedModuleInfo" = { default = null; defaultText = null; path = [ "opt" ]; required = true; }; type = "boolean"; } ]; } ]; inherit default description; }; }; testSubmoduleOption = let subModule = { options.opt = lib.mkOption { type = lib.types.bool; default = true; inherit description; }; }; in { expr = parseOption (evalType (lib.types.submodule subModule) { }); expected = { type = "object"; additionalProperties = false; description = "Test Description"; properties = { opt = { type = "boolean"; default = true; inherit description; }; }; required = [ ]; default = { }; }; }; testSubmoduleOptionWithoutDefault = let subModule = { options.foo = lib.mkOption { type = lib.types.bool; inherit description; }; }; opt = evalType (lib.types.submodule subModule) { }; in { inherit opt; expr = parseOption (opt); expected = { type = "object"; additionalProperties = false; description = "Test Description"; properties = { foo = { type = "boolean"; inherit description; }; }; default = { }; required = [ "foo" ]; }; }; testAttrsOfSubmodule = let subModule = { options.opt = lib.mkOption { type = lib.types.bool; default = true; inherit description; }; }; default = { foo.opt = false; bar.opt = true; }; in { expr = parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default); expected = { type = "object"; additionalProperties = { type = "object"; additionalProperties = false; properties = { opt = { type = "boolean"; default = true; inherit description; }; }; required = [ ]; }; inherit default description; }; }; testListOfSubmodule = let subModule = { options.opt = lib.mkOption { type = lib.types.bool; default = true; inherit description; }; }; default = [ { opt = false; } { opt = true; } ]; in { expr = parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default); expected = { type = "array"; items = { type = "object"; additionalProperties = false; properties = { opt = { type = "boolean"; default = true; inherit description; }; }; required = [ ]; }; inherit default description; }; }; testEither = let default = "foo"; in { expr = parseOption (evalType (lib.types.either lib.types.bool lib.types.str) default); expected = { oneOf = [ { "$exportedModuleInfo" = { path = [ "opt" ]; default = null; defaultText = null; required = true; }; type = "boolean"; } { "$exportedModuleInfo" = { path = [ "opt" ]; default = null; defaultText = null; required = true; }; type = "string"; } ]; inherit default description; }; }; testAnything = let default = "foo"; in { expr = parseOption (evalType lib.types.anything default); expected = { inherit default description; type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; }; }; testUnspecified = let default = "foo"; in { expr = parseOption (evalType lib.types.unspecified default); expected = { inherit default description; type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; }; }; testRaw = let default = "foo"; in { expr = parseOption (evalType lib.types.raw default); expected = { inherit default description; type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; }; }; test_option_with_default_text = { expr = ( parseOption (evalModuleOptions { options.opt = lib.mkOption { type = lib.types.bool; defaultText = "This option is a optional, but we cannot assign a default value to it yet."; }; }) ); expected = { additionalProperties = false; properties = { opt = { type = "boolean"; }; }; # opt is not required, because it has a defaultText required = [ ]; type = "object"; }; }; test_nested_option_with_default_text = { expr = ( parseOption (evalModuleOptions { options.opt = lib.mkOption { type = lib.types.submodule { options = { foo = lib.mkOption { type = lib.types.bool; defaultText = "Not required"; }; }; }; defaultText = "Not required"; }; }) ); expected = { additionalProperties = false; properties = { opt = { additionalProperties = false; properties = { foo = { type = "boolean"; }; }; required = [ ]; type = "object"; }; }; required = [ ]; type = "object"; }; }; # Default MUST not be evaluated if defaultText is present test_default_lazyness = { expr = ( parseOption (evalModuleOptions { options.opt = lib.mkOption { type = lib.types.bool; default = throw "This option is lazy"; defaultText = "This option is lazy"; }; }) ); expected = { additionalProperties = false; properties = { opt = { type = "boolean"; }; }; required = [ ]; type = "object"; }; }; }