From dd7efe91555f126f284a4410dac84239266ba911 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 30 Oct 2024 20:32:33 -0400 Subject: [PATCH] "hey, self. ever heard of YAGNI?" "no, please explain each and every nuance of it in excruciating detail." "eh. the gist is good enough." --- Deployment/ConfigurationBootstrapper.cs | 140 +------ deployment.tests/ConfigTests.cs | 503 +++++++----------------- 2 files changed, 141 insertions(+), 502 deletions(-) diff --git a/Deployment/ConfigurationBootstrapper.cs b/Deployment/ConfigurationBootstrapper.cs index 74cb91e..10da8d9 100644 --- a/Deployment/ConfigurationBootstrapper.cs +++ b/Deployment/ConfigurationBootstrapper.cs @@ -15,20 +15,12 @@ namespace greyn.Deployment { if (File.Exists("appsettings.json")) { - /* "aaaahhh this is the most abstract and reflective thing in the goddamn world aaaahhhhh next you'll make an ECS on a dynamic aaaahhh" - * Ok, yes. I share these fears. but. - * this way we can read a *very* arbitrary configuration file. - * some random external file's configuration values is a good reason to aim for very high flexibility, imo - * if the configuration expects new values we write them in, - * if you left other junk for whatever reason, we don't delete them. + /* + * if the configuration expects new values we write them in. + * if you left other junk for whatever reason, get rekt */ - var actualConfig = JsonConvert.DeserializeObject(File.ReadAllText(confpath)) ?? new ExpandoObject(); - var toReturn = new T(); - PopulateExpando(toReturn, ref actualConfig); - File.WriteAllText(confpath, JsonConvert.SerializeObject(actualConfig, Formatting.Indented)); - - readFromExpando(ref toReturn, actualConfig); - + var toReturn = JsonConvert.DeserializeObject(File.ReadAllText(confpath)) ?? new T(); + File.WriteAllText(confpath, JsonConvert.SerializeObject(toReturn, Formatting.Indented)); return toReturn; } else @@ -38,125 +30,5 @@ namespace greyn.Deployment return toReturn; } } - - //TODO: make private but get tests to cooperate... I think that's a c# limitation - internal static void PopulateExpando(T config, ref ExpandoObject fromFile) - { - if (config == null) return; - if (fromFile == null) - { - fromFile = new ExpandoObject(); - } - var dictionaryFromExpandoFromFile = (IDictionary)fromFile; - - foreach (var memberInfo in config.GetType().GetMembers()) - { - if(memberInfo.DeclaringType == typeof(System.Object)) - { - continue; - } - if(memberInfo.MemberType != MemberTypes.Field && memberInfo.MemberType != MemberTypes.Property) - { - continue; - } - - if (dictionaryFromExpandoFromFile.TryGetValue(memberInfo.Name, out object? valueFromDict)) - { - if (valueFromDict != null) - { - switch (memberInfo.MemberType) - { - case MemberTypes.Field: - var asField = (FieldInfo)memberInfo; - if (dictionaryFromExpandoFromFile[asField.Name] is ExpandoObject childMemberField) - { - PopulateExpando(asField.GetValue(config), ref childMemberField); - } - break; - case MemberTypes.Property: - var asProperty = (PropertyInfo)memberInfo; - if (dictionaryFromExpandoFromFile[asProperty.Name] is ExpandoObject childMemberProperty) - { - PopulateExpando(asProperty.GetValue(config), ref childMemberProperty); - } - break; - default: - break; - } - - } - } - else - { - switch (memberInfo.MemberType) - { - case MemberTypes.Field: - fromFile.TryAdd(memberInfo.Name, ((FieldInfo)memberInfo).GetValue(config)); - break; - case MemberTypes.Property: - fromFile.TryAdd(memberInfo.Name, ((PropertyInfo)memberInfo).GetValue(config)); - break; - default: - break; - } - - } - } - } - internal static void readFromExpando(ref T config, ExpandoObject readFromFile) - { - //TODO: read from Expando - } - - // private static T sync(ref T config, ref ExpandoObject readFromFile) - // { - // var fieldsFoundThisLevel = new List(); - - // if(config == null) - // { - // //TODO: it's entirely possible this is null. - // throw new ArgumentNullException(); - // } - // if(readFromFile == null) - // { - // //TODO: it's entirely possible this is null. - // throw new ArgumentNullException(); - // } - // var readUnExpandoed = (IDictionary)readFromFile; - // //if it's present on what was read - // //set it on Config - // //if it's not found on what was read, or type is different - - // foreach (var prop in config.GetType().GetProperties()) - // { - // fieldsFoundThisLevel.Add(prop.Name); - // if(readUnExpandoed.TryGetValue(prop.Name, out object? valueFromFile)) //so a configuration file can *set* a value *to null* - // { - // if(valueFromFile != null) - // { - // var defaultConfigValue = prop.GetValue(config); - // if (defaultConfigValue != null) - // { - // //expandofy, since we objectified it. - // var expandoFromFile = new ExpandoObject(); - // var dictionaryFromExpandoFromFile = (IDictionary)expandoFromFile; - // foreach (var property in valueFromFile.GetType().GetProperties()) - // dictionaryFromExpandoFromFile.Add(property.Name, property.GetValue(valueFromFile)); - // expandoFromFile = (ExpandoObject)dictionaryFromExpandoFromFile; - // //dive in, repeat - // sync(ref defaultConfigValue, ref expandoFromFile); - // //we've now Sync'd expandoFromFile and defaultConfigValue - // prop.SetValue(readFromFile, expandoFromFile); - // prop.SetValue(config, expandoFromFile); - // } - - // } - // } - // else - // { - // readFromFile.TryAdd(prop.Name, prop.GetValue(config)); - // } - // } - // } } -} \ No newline at end of file +} diff --git a/deployment.tests/ConfigTests.cs b/deployment.tests/ConfigTests.cs index 9ec9786..8bde90a 100644 --- a/deployment.tests/ConfigTests.cs +++ b/deployment.tests/ConfigTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Reflection; using Newtonsoft.Json; using System.Globalization; +using Newtonsoft.Json.Converters; namespace deployment.tests; @@ -33,20 +34,20 @@ public class ConfigTests aValueType: 94.298, aValueTypeButNotAField: 9241, anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] + { + ""test3"": 420.71, + ""test4"": 420.72 + } }, subtyped2: { aValueType: 95.298, aValueTypeButNotAField: 9242, anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] + { + ""test5"": 420.73, + ""test6"": 420.74 + } } }"; private const string EmptyConfiguration = "{}"; @@ -56,321 +57,33 @@ public class ConfigTests }"; #endregion - private ExpandoObject parse(string confstr) - { - return JsonConvert.DeserializeObject(confstr) ?? new ExpandoObject(); - } - - [Test] - public void parseworks() - { - Assert.IsNotNull(parse(PerfectConfiguration)); - } - - [Test] - public void parseworksasexpected() - { - var expandoed = parse(PerfectConfiguration); - Assert.IsNotNull(expandoed); - var casted = (IDictionary)expandoed; - Assert.IsNotNull(casted); - //Console.WriteLine(JsonConvert.SerializeObject(casted, Formatting.Indented)); - } - - #region jokes - [Test] - public void fuckit_werealreadytestingthetest_letstestthattest() - { - parseworks(); - Assert.Pass(); - } - [Test] - public void more_tests_more_good() - { - fuckit_werealreadytestingthetest_letstestthattest(); - Assert.Pass(); - } - [Test] - public void what_do_you_mean_a_bug_escaped_tests() - { - more_tests_more_good(); - Assert.Pass(); - } - [Test] public void vibecheck() { - Assert.NotZero(new Random().Next(0, 2)); + Assert.AreEqual(0, new Random().Next(0, 2)); } - [Test] - public void populateExpando_doesnt_explode() + [TearDown] + public void TearDown() { - var toReturn = new AConfigurationType(); - var actualConfig = parse(PerfectConfiguration); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(toReturn, ref actualConfig); - Assert.Pass(); + if(File.Exists("appsettings.json")) + File.Delete("appsettings.json"); } - #endregion - + [Test] - public void populateExpando_getsmissing_property() + public void load_doesnt_explode() { - var actualConfig = parse(@"{ - aField: ""I've decided"", - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - Console.WriteLine("survived populate expando"); - var casted = (IDictionary)actualConfig; - Console.WriteLine(casted["aValueTypeButNotAField"]); - Assert.Pass(); - } - - [Test] - public void populateExpando_getsmissing_field() - { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - Console.WriteLine(casted["aField"]); + File.WriteAllText("appsettings.json", PerfectConfiguration); + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); Assert.Pass(); } [Test] - public void populateExpando_getsmissing_subobject() + public void load_loads() { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - Console.WriteLine(casted["subtyped"]); - Assert.Pass(); - } - [Test] - public void populateExpando_getsmissing_subobject_field() - { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped: - { - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - var subtypeCasted = (IDictionary)casted["subtyped"]; - Console.WriteLine(subtypeCasted["aValueType"]); - Assert.Pass(); - } - [Test] - public void populateExpando_getsmissing_subobject_property() - { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped: - { - aValueType: 94.298, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - var subtypeCasted = (IDictionary)casted["subtyped"]; - Console.WriteLine(subtypeCasted["aValueTypeButNotAField"]); - Assert.Pass(); - } - [Test] - public void populateExpando_getsmissing_subobject_enumerable() - { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - var subtypeCasted = (IDictionary)casted["subtyped"]; - Console.WriteLine(subtypeCasted["anEnumerableType"]); - Assert.Pass(); - } - [Test] - public void populateExpando_doesntoverridenullednullable() - { - var actualConfig = parse(@"{ - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped: null, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - Assert.IsNull(casted["subtyped"]); - } - [Test] - public void populateExpando_doesntremoveextras() - { - var actualConfig = parse(@"{ - hiImHereToo: ""sup"", - aValueTypeButNotAField: 796.651, - aField: ""I've decided"", - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - var casted = (IDictionary)actualConfig; - Assert.IsNotNull(casted["hiImHereToo"]); - } - [Test] - public void populateExpando_populatesblank() - { - var actualConfig = parse("{}"); - greyn.Deployment.ConfigurationBootstrapper.PopulateExpando(new AConfigurationType(), ref actualConfig); - dynamic casted = actualConfig; - Console.WriteLine(JsonConvert.SerializeObject(casted, Formatting.Indented)); + File.WriteAllText("appsettings.json", PerfectConfiguration); + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); - Console.WriteLine(casted.subtyped.aValueTypeButNotAField); - Assert.Pass(); - } - [Test] - public void readfromExpando_doesnt_explode() - { - var toReturn = new AConfigurationType(); - var actualConfig = parse(PerfectConfiguration); - greyn.Deployment.ConfigurationBootstrapper.readFromExpando(ref toReturn, actualConfig); - Assert.Pass(); - } - [Test] - public void readfromExpando_does() - { - var readConfig = parse(PerfectConfiguration); - var conf = new AConfigurationType(); - greyn.Deployment.ConfigurationBootstrapper.readFromExpando(ref conf, readConfig); - Assert.AreEqual(conf.aValueTypeButNotAField, 796.651); + Assert.AreEqual(conf.aValueTypeButNotAField, 796.651f); Assert.AreEqual(conf.aField, "I've decided"); Assert.AreEqual(conf.subtyped.aValueType, 94.298); Assert.AreEqual(conf.subtyped.aValueTypeButNotAField, 9241); @@ -380,18 +93,40 @@ public class ConfigTests Assert.IsNotNull(conf.subtyped2); } [Test] - public void readfromExpando_acceptsnull() + public void load_acceptsnullasvalue() { - var readConfig = parse(PerfectConfiguration); - var conf = new AConfigurationType(); - greyn.Deployment.ConfigurationBootstrapper.readFromExpando(ref conf, readConfig); + File.WriteAllText("appsettings.json", @"{ + aValueTypeButNotAField: 796.651, + aField: ""I've decided"", + subtyped: + { + aValueType: 94.298, + aValueTypeButNotAField: 9241, + anEnumerableType: null + }, + subtyped2: + { + aValueType: 95.298, + aValueTypeButNotAField: 9242, + anEnumerableType: + { + ""test5"": 420.73, + ""test6"": 420.74 + } + } + }"); - Assert.AreEqual(conf.aValueTypeButNotAField, 796.651); + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); + + + Assert.AreEqual(conf.aValueTypeButNotAField, 796.651f); Assert.AreEqual(conf.aField, "I've decided"); Assert.AreEqual(conf.subtyped.aValueType, 94.298); Assert.AreEqual(conf.subtyped.aValueTypeButNotAField, 9241); Assert.IsNotNull(conf.subtyped); + Assert.IsNull(conf.subtyped.anEnumerableType); + Assert.IsNotNull(conf.subtyped2); Assert.AreEqual(conf.subtyped2.aValueType, 95.298); Assert.AreEqual(conf.subtyped2.aValueTypeButNotAField, 9242); @@ -400,35 +135,67 @@ public class ConfigTests Assert.AreEqual(conf.subtyped2.anEnumerableType["test6"], 420.74); } [Test] - public void readfromExpando_usesdefaultfornullablemissing() + public void load_acceptsnull_forparenttype() { - var readConfig = parse(@"{ - aValueTypeButNotAField: 796.651, + File.WriteAllText("appsettings.json", @"{ + aValueTypeButNotAField: 796.651, + aField: ""I've decided"", + subtyped: null, + subtyped2: + { + aValueType: 95.298, + aValueTypeButNotAField: 9242, + anEnumerableType: + { + ""test5"": 420.73, + ""test6"": 420.74 + } + } + }"); + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - var conf = new AConfigurationType(); - greyn.Deployment.ConfigurationBootstrapper.readFromExpando(ref conf, readConfig); - Assert.AreEqual(conf.aValueTypeButNotAField, 796.651); + Assert.AreEqual(conf.aValueTypeButNotAField, 796.651f); + Assert.AreEqual(conf.aField, "I've decided"); + + Assert.IsNull(conf.subtyped); + + Assert.IsNotNull(conf.subtyped2); + Assert.AreEqual(conf.subtyped2.aValueType, 95.298); + Assert.AreEqual(conf.subtyped2.aValueTypeButNotAField, 9242); + Assert.IsNotNull(conf.subtyped2.anEnumerableType); + Assert.AreEqual(conf.subtyped2.anEnumerableType["test5"], 420.73); + Assert.AreEqual(conf.subtyped2.anEnumerableType["test6"], 420.74); + } + [Test] + public void load_usesdefaultfornullablemissing() + { + File.WriteAllText("appsettings.json", @"{ + aValueTypeButNotAField: 796.651, + + subtyped: + { + aValueType: 94.298, + aValueTypeButNotAField: 9241, + anEnumerableType: + { + ""test3"": 420.71, + ""test4"": 420.72 + } + }, + subtyped2: + { + aValueType: 95.298, + aValueTypeButNotAField: 9242, + anEnumerableType: + { + ""test5"": 420.73, + ""test6"": 420.74 + } + } + }"); + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); + + Assert.AreEqual(conf.aValueTypeButNotAField, 796.651f); Assert.AreEqual(conf.aField, "hi there, hello"); Assert.AreEqual(conf.subtyped.aValueType, 94.298); Assert.AreEqual(conf.subtyped.aValueTypeButNotAField, 9241); @@ -438,34 +205,34 @@ public class ConfigTests Assert.IsNotNull(conf.subtyped2); } [Test] - public void readfromExpando_usesdefaultfornonnullablemissing() + public void load_usesdefaultfornonnullablemissing() { - var readConfig = parse(@"{ - - aField: ""I've decided"", - subtyped: - { - aValueType: 94.298, - aValueTypeButNotAField: 9241, - anEnumerableType: - [ - {""test3"": 420.71}, - {""test4"": 420.72} - ] - }, - subtyped2: - { - aValueType: 95.298, - aValueTypeButNotAField: 9242, - anEnumerableType: - [ - {""test5"": 420.73}, - {""test6"": 420.74} - ] - } - }"); - var conf = new AConfigurationType(); - greyn.Deployment.ConfigurationBootstrapper.readFromExpando(ref conf, readConfig); + File.WriteAllText("appsettings.json", @"{ + aField: ""I've decided"", + subtyped: + { + aValueType: 94.298, + aValueTypeButNotAField: 9241, + anEnumerableType: + { + ""test3"": 420.71, + ""test4"": 420.72 + } + }, + subtyped2: + { + aValueType: 95.298, + aValueTypeButNotAField: 9242, + anEnumerableType: + { + ""test5"": 420.73, + ""test6"": 420.74 + } + } + }"); + + var conf = greyn.Deployment.ConfigurationBootstrapper.Load(); + Assert.AreEqual(conf.aValueTypeButNotAField, 156.697f); Assert.AreEqual(conf.aField, "I've decided"); Assert.AreEqual(conf.subtyped.aValueType, 94.298);