public void when_building_config_then_file_can_be_missing() { var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var config = Config.Build(dir); Assert.False(File.Exists(config.FilePath)); }
public void can_load() { var file = Path.GetTempFileName(); File.WriteAllText(file, @" [sleet] feedLockTimeoutMinutes = 30 username = kzu useremail = [email protected] [sleet ""AzureFeed""] type = azure container = feed connectionString = ""DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=;BlobEndpoint="" path = https://myaccount.blob.core.windows.net/feed/ feedSubPath = subPath [sleet ""AmazonFeed""] type = s3 bucketName = bucket region = us-east-1 profileName = profile path = https://s3.amazonaws.com/my-bucket/ feedSubPath = subPath serverSideEncryptionMethod = AES256 compress [sleet ""FolderFeed""] type = local path = C:\\feed "); Config.Build(file); }
public void can_read_hierarchical_with_config_entry_level() { var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local")); foreach (var entry in config.GetRegex("core")) { switch (entry.Variable) { case "local": case "parent": Assert.Null(entry.Level); break; case "global": Assert.Equal(ConfigLevel.Global, entry.Level); break; case "system": Assert.Equal(ConfigLevel.System, entry.Level); break; default: break; } } }
public void can_read_hierarchical_from_root_file() { var config = Config.Build("C:\\.netconfig"); Assert.True(config.GetBoolean("core", "global")); Assert.True(config.GetBoolean("core", "system")); }
public void can_read_hierarchical_from_relative_path() { var config = Config.Build(Config.FileName); Assert.True(config.GetBoolean("core", "global")); Assert.True(config.GetBoolean("core", "system")); }
public void can_write_new_file() { var file = Path.GetTempFileName(); var config = Config.Build(file) .SetBoolean("section", "subsection", "bool", true); Assert.True(config.GetBoolean("section", "subsection", "bool")); }
public void can_read_global() { var config = Config.Build(ConfigLevel.Global); Assert.Null(config.GetBoolean("core", "local")); Assert.Null(config.GetBoolean("core", "parent")); Assert.True(config.GetBoolean("core", "global")); Assert.True(config.GetBoolean("core", "system")); }
public void when_reading_non_existent_file_then_values_are_null() { var config = Config.Build(Path.GetTempFileName()); Assert.Null(config.GetBoolean("core", "bool")); Assert.Null(config.GetBoolean("core", "bool")); Assert.Null(config.GetString("core", "string")); Assert.Null(config.GetNumber("core", "int")); Assert.Null(config.GetNumber("core", "int")); }
public void given_command_tree_sets_all_default() { var config = Config.Build(Path.GetTempFileName()) .SetString("foo", "name", "foo") .SetString("foo", "bar", "name", "bar") .SetBoolean("foo", "baz", "force", true) .SetString("foo", "baz.update", "name", "baz"); var command = new Command("foo") { new Command("bar") { new Argument <string?>("name"), }, new Command("baz") { new Command("update") { new Argument <string?>("name"), }, new Command("install") { new Argument <string?>("name"), }, new Command("uninstall") { new Argument <string?>("name"), new Option <bool?>("force"), }, }, }.WithConfigurableDefaults(configuration: config); Assert.True(Traverse .BreadthFirst(command, c => c.Children.OfType <Command>()) .SelectMany(x => x.Arguments).All(x => x.HasDefaultValue)); // Gets from the command-specific section Assert.Equal("bar", command.Children.OfType <Command>().First().Arguments.First().GetDefaultValue()); Assert.Equal("baz", Traverse .DepthFirst(command, c => c.Children.OfType <Command>()) .First(c => c.Name == "update") .Arguments.First().GetDefaultValue()); // Uses default from lifted top-level section for shared "name" argument Assert.Equal("foo", Traverse .DepthFirst(command, c => c.Children.OfType <Command>()) .First(c => c.Name == "install") .Arguments.First().GetDefaultValue()); // Non-shared but still lifted since no conflicts Assert.True(Traverse .DepthFirst(command, c => c.Children.OfType <Command>()) .First(c => c.Name == "uninstall") .Options.OfType <IOption>().First().Argument.GetDefaultValue() as bool?); }
public void can_roundtrip() { var file = Path.GetTempFileName(); var config = Config.Build(file); config.SetString("section", "subsection", "foo", "bar"); var value = Config.Build(file).GetString("section", "subsection", "foo"); Assert.Equal("bar", value); }
public async Task reading_from_multiple_threads_succeeds() { Config.GlobalLocation = Path.Combine(Directory.GetCurrentDirectory(), "Content", "global.netconfig"); Config.SystemLocation = Path.Combine(Directory.GetCurrentDirectory(), "Content", "system.netconfig"); CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; var path = Path.Combine(Directory.GetCurrentDirectory(), "Content", ".netconfig"); await Task.WhenAll(Enumerable .Range(0, 5) .Select(_ => Task.Run(() => Config.Build(path))) .ToArray()); }
public void can_save_values() { var config = BuildConfiguration(); config["foo:enabled"] = "true"; config["foo:bar:baz"] = "bye"; config["http:https://weaker.example.com:sslVerify"] = "false"; var dotnet = Config.Build(Path.Combine(Constants.CurrentDirectory, "Content", "web", nameof(can_save_values))); Assert.Equal("true", dotnet.GetString("foo", "enabled")); Assert.Equal("bye", dotnet.GetString("foo", "bar", "baz")); Assert.Equal("false", dotnet.GetString("http", "https://weaker.example.com", "sslVerify")); }
public void when_reading_local_file_as_root_then_parent_variable_is_null() { var path = Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "Content", Guid.NewGuid().ToString())); Directory.CreateDirectory(path); var config = Config.Build(path); config.SetBoolean("config", "root", true); // Reload after the value was set. config = Config.Build(path); Assert.Null(config.GetBoolean("config", "parent")); }
public void when_reading_local_file_with_system_false_then_system_variable_is_null() { var path = Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "Content", Guid.NewGuid().ToString())); Directory.CreateDirectory(path); var config = Config.Build(path); config.SetBoolean("config", "system", false); // Reload after the value was set. config = Config.Build(path); Assert.Null(config.GetBoolean("core", "system")); }
public void when_setting_local_variable_then_writes_user_file() { Config.GlobalLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); Config.SystemLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); var path = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())).FullName; var config = Config.Build(path); config.AddString("foo", "bar", "baz", ConfigLevel.Local); Assert.True(File.Exists(Path.Combine(path, Config.FileName + ".user"))); using var reader = new ConfigReader(Path.Combine(path, Config.FileName + ".user")); Assert.Single(reader.ReadAllLines().Where(x => x.Kind == LineKind.Variable && x.Variable == "bar" && x.Value == "baz")); }
public void when_setting_variable_in_global_dir_then_writes_global_file() { Config.GlobalLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); Config.SystemLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); // Simulates opening a cli app from the user's profile dir Directory.CreateDirectory(Path.GetDirectoryName(Config.GlobalLocation) !); Directory.SetCurrentDirectory(Path.GetDirectoryName(Config.GlobalLocation) !); var config = Config.Build(); config.SetString("foo", "bar", "baz"); var global = Config.Build(ConfigLevel.Global); Assert.Equal("baz", global.GetString("foo", "bar")); }
public void can_read_hierarchical() { var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local")); Assert.True(config.GetBoolean("core", "local")); Assert.True(config.GetBoolean("core", "parent")); Assert.True(config.GetBoolean("core", "global")); Assert.True(config.GetBoolean("core", "system")); var section = config.GetSection("core"); Assert.True(section.GetBoolean("local")); Assert.True(section.GetBoolean("parent")); Assert.True(section.GetBoolean("global")); Assert.True(section.GetBoolean("system")); }
public void given_long_array_sets_all_values() { var config = Config.Build(Path.GetTempFileName()) .AddNumber("cli", "include", 25) .AddNumber("cli", "include", 50) .AddNumber("cli", "include", 100); var command = new RootCommand() { new Argument <long[]>("include"), }.WithConfigurableDefaults("cli", configuration: config); command.Handler = CommandHandler.Create <long[]>(include => Assert.Equal(new long[] { 25, 50, 100 }, include)); new CommandLineBuilder(command).Build().Invoke(new string[] { }); }
public void when_setting_local_variable_then_overrides_default_file() { Config.GlobalLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); Config.SystemLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); var path = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())).FullName; File.WriteAllText(Path.Combine(path, ".netconfig"), @"[foo] bar = baz"); File.WriteAllText(Path.Combine(path, ".netconfig.user"), @"[foo] bar = hey"); var config = Config.Build(path); Assert.Equal("hey", config.GetString("foo", "bar")); }
public void when_setting_system_variable_then_writes_global_file() { Config.GlobalLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); Config.SystemLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), ".netconfig"); var path = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())).FullName; File.WriteAllText(Path.Combine(path, ".netconfig"), ""); var config = Config.Build(path); config.AddBoolean("test", "var", true, ConfigLevel.System); var global = Config.Build(ConfigLevel.System); Assert.True(global.GetBoolean("test", "var")); }
public void when_reading_hierarchical_from_file_reads_system_once() { Config.SystemLocation = Path.Combine(Directory.GetCurrentDirectory(), "Content", ".netconfig"); var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local", ".netconfig")); Assert.True(config.GetBoolean("core", "local")); Assert.True(config.GetBoolean("core", "parent")); var section = config.GetSection("core"); Assert.True(section.GetBoolean("local")); Assert.True(section.GetBoolean("parent")); Assert.True(section.GetBoolean("global")); // Reads the system.netconfig only once, even if the file is in a subfolder Assert.Single(config.GetAll("core", "parent")); }
public void saves_to_same_level() { var dir = Path.Combine(Constants.CurrentDirectory, "Content", "web", nameof(saves_to_same_level)); Directory.CreateDirectory(dir); var usr = Path.Combine(dir, Config.FileName + Config.UserExtension); Config.Build(usr).SetNumber("local", "value", 999, ConfigLevel.Local); var config = BuildConfiguration(); Assert.Equal("999", config["local:value"]); config["local:value"] = "888"; Assert.Equal("888", config["local:value"]); Assert.Contains("888", File.ReadAllText(usr)); }
public void when_resolving_path_then_resolves_relative_to_config() { var globalDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var systemDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var parentDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var localDir = Path.Combine(parentDir, Guid.NewGuid().ToString()); Config.GlobalLocation = Path.Combine(globalDir, ".netconfig"); Config.SystemLocation = Path.Combine(systemDir, ".netconfig"); Directory.CreateDirectory(globalDir); Directory.CreateDirectory(systemDir); Directory.CreateDirectory(localDir); File.WriteAllText(Config.GlobalLocation, @$ "[file] global = .\\global\\file.txt dir = .\\global "); File.WriteAllText(Config.SystemLocation, @$ "[file] system = ./system/file.txt "); File.WriteAllText(Path.Combine(parentDir, Config.FileName), @$ "[file] parent = file.txt "); File.WriteAllText(Path.Combine(localDir, Config.FileName), @$ "[file] local = ./file.txt "); var config = Config.Build(localDir); Assert.Equal(Path.Combine(globalDir, "global"), config.GetNormalizedPath("file", "dir")); Assert.Equal(Path.Combine(globalDir, "global", "file.txt"), config.GetNormalizedPath("file", "global")); Assert.Equal(Path.Combine(systemDir, "system", "file.txt"), config.GetNormalizedPath("file", "system")); Assert.Equal(Path.Combine(parentDir, "file.txt"), config.GetNormalizedPath("file", "parent")); Assert.Equal(Path.Combine(localDir, "file.txt"), config.GetNormalizedPath("file", "local")); var section = config.GetSection("file"); Assert.Equal(Path.Combine(globalDir, "global"), section.GetNormalizedPath("dir")); Assert.Equal(Path.Combine(globalDir, "global", "file.txt"), section.GetNormalizedPath("global")); Assert.Equal(Path.Combine(systemDir, "system", "file.txt"), section.GetNormalizedPath("system")); Assert.Equal(Path.Combine(parentDir, "file.txt"), section.GetNormalizedPath("parent")); Assert.Equal(Path.Combine(localDir, "file.txt"), section.GetNormalizedPath("local")); }
public void given_string_array_sets_all_values() { var config = Config.Build(Path.GetTempFileName()) .AddString("cli", "include", "foo") .AddString("cli", "include", "bar") .AddString("cli", "include", "baz"); var command = new RootCommand() { new Argument <string[]>("include"), }.WithConfigurableDefaults("cli", configuration: config); Assert.Equal(new[] { "foo", "bar", "baz" }, (string[]?)command.Arguments.First().GetDefaultValue()); command.Handler = CommandHandler.Create <string[]>(include => Assert.Equal(new[] { "foo", "bar", "baz" }, include)); new CommandLineBuilder(command).Build().Invoke(new string[] { }); }
static int Run(string[] args) { var help = false; var action = ConfigAction.None; var useSystem = false; var useGlobal = false; var useLocal = false; var path = Directory.GetCurrentDirectory(); var nameOnly = false; string?defaultValue = default; string?type = default; var debug = false; var options = new OptionSet { { Environment.NewLine }, { Environment.NewLine }, { "Location (uses all locations by default)" }, { "local", "use .netconfig.user file", _ => useLocal = true }, { "global", "use global config file", _ => useGlobal = true }, { "system", "use system config file", _ => useSystem = true }, { "path:", "use given config file or directory", f => path = f }, { Environment.NewLine }, { "Action" }, { "get", "get value: name [value-regex]", _ => action = ConfigAction.Get }, { "get-all", "get all values: key [value-regex]", _ => action = ConfigAction.GetAll }, { "get-regexp", "get values for regexp: name-regex [value-regex]", _ => action = ConfigAction.GetRegexp }, { "set", "set value: name value [value-regex]", _ => action = ConfigAction.Set }, { "set-all", "set all matches: name value [value-regex]", _ => action = ConfigAction.SetAll }, { "replace-all", "replace all matches: name value [value-regex]", _ => action = ConfigAction.SetAll, true }, //{ "get-urlmatch", "get value specific for the URL: section[.var] URL", _ => action = ConfigAction.Get }, { "add", "add a new variable: name value", _ => action = ConfigAction.Add }, { "unset", "remove a variable: name [value-regex]", _ => action = ConfigAction.Unset }, { "unset-all", "remove all matches: name [value-regex]", _ => action = ConfigAction.UnsetAll }, { "remove-section", "remove a section: name", _ => action = ConfigAction.RemoveSection }, { "rename-section", "rename section: old-name new-name", _ => action = ConfigAction.RenameSection }, { "l|list", "list all", _ => action = ConfigAction.List }, { "e|edit", "edit the config file in an editor", _ => action = ConfigAction.Edit }, { Environment.NewLine }, { "Other" }, { "default:", "with --get, use default value when missing entry", v => defaultValue = v }, { "name-only", "show variable names only", _ => nameOnly = true }, { "type:", "value is given this type, can be 'boolean', 'datetime' or 'number'", t => type = t }, { "debug", "add some extra logging for troubleshooting purposes", _ => debug = true, true }, { "?|h|help", "Display this help", h => help = h != null }, }; var extraArgs = options.Parse(args); if (debug) { Console.WriteLine($"::debug::args[{args.Length}]:: {string.Join(" ", args.Select(x => x.IndexOf(' ') != -1 ? $"\"{x}\"" : x))}"); Console.WriteLine($"::debug::extraargs[{extraArgs.Count}]:: {string.Join(" ", extraArgs.Select(x => x.IndexOf(' ') != -1 ? $"\"{x}\"" : x))}"); } if (args.Length == 1 && help) { return(ShowHelp(options)); } // Can only use one location if ((useGlobal && (useSystem || useLocal)) || (useSystem && (useGlobal || useLocal)) || (useLocal && (useGlobal || useSystem))) { return(ShowError("Can only specify one config location.")); } ConfigLevel?level = null; Config config; if (useGlobal) { config = Config.Build(ConfigLevel.Global); level = ConfigLevel.Global; } else if (useSystem) { config = Config.Build(ConfigLevel.System); level = ConfigLevel.System; } else { config = Config.Build(path); if (useLocal) { level = ConfigLevel.Local; } } // Can be a get or a set, depending on whether a value is provided. if (action == ConfigAction.None) { if (extraArgs.Count == 1) { action = ConfigAction.Get; } else if (extraArgs.Count > 1) { action = ConfigAction.Set; } else { return(ShowHelp(options)); } } // TODO: For any action that isn't a simple get (which may rely on the output being just the // value retrieved), render to the output window the warnings about invalid entries Action <ConfigEntry> entryWriter; if (nameOnly) { entryWriter = e => Console.WriteLine(e.Key); } else { entryWriter = e => Console.WriteLine($"{e.Key}={(e.RawValue == null ? "" : e.RawValue.Contains(' ') ? "\"" + e.RawValue + "\"" : e.RawValue)}"); } if (type == "date") { type = "datetime"; } if (type == "bool") { type = "boolean"; } var kind = ValueKind.String; if (type != null && !Enum.TryParse(type, true, out kind)) { Console.Error.WriteLine($"Error: invalid type '{type}'. Expected one of: 'boolean', 'bool', 'datetime', 'date' or 'number'."); return(-1); } string?error = default; switch (action) { case ConfigAction.Add: { TextRules.ParseKey(extraArgs[0], out var section, out var subsection, out var variable); if (extraArgs.Count != 2) { return(ShowHelp(options)); } if ((kind == ValueKind.Boolean && !string.IsNullOrEmpty(extraArgs[1]) && !TextRules.TryValidateBoolean(extraArgs[1], out error)) || (kind == ValueKind.Number && !TextRules.TryValidateNumber(extraArgs[1], out error)) || kind == ValueKind.DateTime && !DateTime.TryParse(extraArgs[1], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out _)) { if (error != null) { return(ShowError(error)); } return(ShowError($"Unexpected datetime value `{extraArgs[1]}`, expected ISO 8601 (aka round-trip) format.")); } if (level != null) { config.AddString(section !, subsection, variable !, extraArgs[1], level.Value); } else { config.AddString(section !, subsection, variable !, extraArgs[1]); } break; } case ConfigAction.Get: { TextRules.ParseKey(extraArgs[0], out var section, out var subsection, out var variable); var value = config.GetString(section !, subsection, variable !); Console.WriteLine(value ?? defaultValue ?? ""); break; } case ConfigAction.GetAll: { TextRules.ParseKey(extraArgs[0], out var section, out var subsection, out var variable); string?valueRegex = default; if (extraArgs.Count > 1) { valueRegex = extraArgs[1]; } foreach (var entry in config.GetAll(section !, subsection, variable !, valueRegex)) { entryWriter(entry); } break; } case ConfigAction.GetRegexp: { if (extraArgs.Count == 0 || extraArgs.Count > 2) { return(ShowHelp(options)); } var nameRegex = extraArgs[0]; string?valueRegex = default; if (extraArgs.Count > 1) { valueRegex = extraArgs[1]; } foreach (var entry in config.GetRegex(nameRegex, valueRegex)) { entryWriter(entry); } break; } case ConfigAction.Set: { TextRules.ParseKey(extraArgs[0], out var section, out var subsection, out var variable); var value = string.Join(' ', extraArgs.Skip(1)).Trim(); // It's a common mistake to do 'config key = value', so just remove it. if (value.StartsWith('=')) { value = new string(value[1..]).Trim();
public void can_read_local_file() { var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local", ".netconfig")); Assert.True(config.GetBoolean("core", "local")); }
public void when_reading_local_file_system_variable_is_set() { var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local", ".netconfig")); Assert.True(config.GetBoolean("core", "system")); }
public void when_build_hierarchical_filepath_is_default() { var config = Config.Build(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local")); Assert.Equal(Path.Combine(Directory.GetCurrentDirectory(), "Content", "local", Config.FileName), config.FilePath); }