|
|
using OptionStudy.UnitApp.Next;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
namespace OptionStudy.Next
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// Json文件 配置源
|
|
|
/// </summary>
|
|
|
public class JsonConfigurationSourceTest : IDisposable
|
|
|
{
|
|
|
private readonly ITestOutputHelper testOutput;
|
|
|
|
|
|
public JsonConfigurationSourceTest(ITestOutputHelper testOutputHelper)
|
|
|
{
|
|
|
this.testOutput = testOutputHelper;
|
|
|
}
|
|
|
|
|
|
#region 空值测试
|
|
|
/// <summary>
|
|
|
/// Key值为没有内容的空对象{}
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void EmptyObject_AddsAsNull_Test()
|
|
|
{
|
|
|
var json = @"{""key"": { }}";
|
|
|
|
|
|
var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
|
|
|
jsonProvider.Load(StringToStream(json));
|
|
|
|
|
|
Assert.True(jsonProvider.TryGet("key",out string? keyValue));
|
|
|
Assert.Null(keyValue);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// key值为null
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void NullObject_AddsEmptyString_Test()
|
|
|
{
|
|
|
var json = @"{ ""key"": null}";
|
|
|
|
|
|
var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
|
|
|
jsonProvider.Load(StringToStream(json));
|
|
|
|
|
|
Assert.True(jsonProvider.TryGet("key", out string? value));
|
|
|
Assert.Equal("", value);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 没有父类的嵌套项
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void NestedObject_DoesNotAddParent_Test()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""key"":
|
|
|
{
|
|
|
""nested"": ""value""
|
|
|
}
|
|
|
}
|
|
|
";
|
|
|
|
|
|
var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
|
|
|
jsonProvider.Load(StringToStream(json));
|
|
|
|
|
|
Assert.False(jsonProvider.TryGet("key", out _));
|
|
|
Assert.True(jsonProvider.TryGet("key:nested",out string? nestedValue));
|
|
|
Assert.Equal("value", nestedValue);
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取json数据源,从json文本
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void BuildConfigurationSource_FromJsonStream()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""firstname"": ""test"",
|
|
|
""test.last.name"": ""last.name"",
|
|
|
""residential.address"": {
|
|
|
""street.name"": ""Something street"",
|
|
|
""zipcode"": ""12345""
|
|
|
}
|
|
|
}";
|
|
|
|
|
|
var config = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Equal("test", config["firstname"]);
|
|
|
Assert.Equal("last.name", config["test.last.name"]);
|
|
|
Assert.Equal("Something street", config["residential.address:STREET.name"]);
|
|
|
Assert.Equal("12345", config["residential.address:zipcode"]);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// JsonStream 为 null时,构建配置源异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void BuildConfigurationSource_FromNullJsonStream()
|
|
|
{
|
|
|
Assert.Throws<InvalidOperationException>(() =>
|
|
|
{
|
|
|
_ = new ConfigurationBuilder().AddJsonStream(null).Build();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 提供者为 JsonStreamProvider (流提供者)时,配置源重新加载异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_ReloadThrows_FromStreamProvider()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""firstname"": ""test""
|
|
|
}";
|
|
|
var config = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => config.Reload());
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 从有效的json中加载键值对
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_LoadKeyValuePairs_FromValidJson()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""firstname"": ""test"",
|
|
|
""test.last.name"": ""last.name"",
|
|
|
""residential.address"": {
|
|
|
""street.name"": ""Something street"",
|
|
|
""zipcode"": ""12345""
|
|
|
}
|
|
|
}";
|
|
|
|
|
|
var jsonSource = new JsonStreamConfigurationSource ();
|
|
|
jsonSource.Stream = StringToStream(json);
|
|
|
|
|
|
var jsonPorvider = jsonSource.Build(new ConfigurationBuilder()) as JsonStreamConfigurationProvider;
|
|
|
//jsonPorvider?.Load(StringToStream(json));
|
|
|
|
|
|
var root = new ConfigurationBuilder()
|
|
|
.Add(jsonSource)
|
|
|
//.Add(jsonPorvider.Source) //或者这种写法
|
|
|
.Build();
|
|
|
|
|
|
//推荐使用下面的AddJsonStream()扩展方法,上面只是展示了一步步构建 ConfigurationRoot 的过程
|
|
|
//new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Equal("test", root.GetValue<string>("firstname"));
|
|
|
Assert.Equal("last.name", root.GetValue<string>("test.last.name"));
|
|
|
Assert.Equal("Something street", root.GetValue<string>("residential.address:STREET.name"));
|
|
|
Assert.Equal("12345", root.GetValue<string>("residential.address:zipcode"));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取空字符串值
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_LoadEmptyValue_Test()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""name"": """"
|
|
|
}";
|
|
|
|
|
|
var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Equal(string.Empty, configurationRoot.GetValue<string>("name"));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 按文化区域获取配置项
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_LoadWithCulture()
|
|
|
{
|
|
|
var previousCulture = CultureInfo.CurrentCulture;
|
|
|
|
|
|
try
|
|
|
{
|
|
|
CultureInfo.CurrentCulture = new CultureInfo("fr-FR");
|
|
|
|
|
|
var json = @"
|
|
|
{
|
|
|
""number"": 3.14
|
|
|
}";
|
|
|
|
|
|
var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
Assert.Equal("3.14", configurationRoot.GetValue<string>("number"));
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
CultureInfo.CurrentCulture = previousCulture;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Json无根元素时,抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_NonObjectRootIsInvalid()
|
|
|
{
|
|
|
var json = @"""test""";
|
|
|
|
|
|
var exception = Assert.Throws<FormatException>(() => LoadProvider(json));
|
|
|
|
|
|
Assert.NotNull(exception.Message);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 支持并忽略 json 中的注释
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_SupportAndIgnoreComments()
|
|
|
{
|
|
|
var json = @"/* 注释将被忽略后,正常解析 */
|
|
|
{/* Comments */
|
|
|
""name"": /* Comments */ ""test"",
|
|
|
""address"": {
|
|
|
""street"": ""Something street"", /* Comments */
|
|
|
""zipcode"": ""12345""
|
|
|
}
|
|
|
}";
|
|
|
|
|
|
var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Equal("test", configurationRoot.GetValue<string>("name"));
|
|
|
Assert.Equal("Something street", configurationRoot.GetValue<string>("address:street"));
|
|
|
Assert.Equal("12345", configurationRoot.GetValue<string>("address:zipcode"));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 支持并忽略json尾部逗号
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_SupportAndIgnoreTrailingCommas()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""firstname"": ""test"",
|
|
|
""test.last.name"": ""last.name"",
|
|
|
""residential.address"": {
|
|
|
""street.name"": ""Something street"",
|
|
|
""zipcode"": ""12345"",
|
|
|
},
|
|
|
}";
|
|
|
|
|
|
var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
|
|
|
|
|
|
Assert.Equal("test", configurationRoot.GetValue<string>("firstname"));
|
|
|
Assert.Equal("last.name", configurationRoot.GetValue<string>("test.last.name"));
|
|
|
Assert.Equal("Something street", configurationRoot.GetValue<string>("residential.address:STREET.name"));
|
|
|
Assert.Equal("12345", configurationRoot.GetValue<string>("residential.address:zipcode"));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 在完成分析之前发现意外结束时抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationProvider_ThrowExceptionWhenUnexpectedEndFoundBeforeFinishParsing()
|
|
|
{
|
|
|
var json = @"{
|
|
|
""name"": ""test"",
|
|
|
""address"": {
|
|
|
""street"": ""Something street"",
|
|
|
""zipcode"": ""12345""
|
|
|
}
|
|
|
/* Missing a right brace here*/";
|
|
|
var exception = Assert.Throws<FormatException>(() =>
|
|
|
{
|
|
|
LoadProvider(json);
|
|
|
});
|
|
|
Assert.Contains("Could not parse the JSON file.", exception.Message);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 在完成分析之前缺少封闭大括号时抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationProvider_ThrowExceptionWhenMissingCurlyBeforeFinishParsing()
|
|
|
{
|
|
|
var json = @"
|
|
|
{
|
|
|
""Data"": {
|
|
|
";
|
|
|
|
|
|
var exception = Assert.Throws<FormatException>(() => LoadProvider(json));
|
|
|
Assert.Contains("Could not parse the JSON file.", exception.Message);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 文件路径为null时,抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_ThrowExceptionWhenPassingNullAsFilePath()
|
|
|
{
|
|
|
Assert.Throws<ArgumentException>(() => new ConfigurationBuilder().AddJsonFile(path: null));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 文件路径为空字符串时,抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_ThrowExceptionWhenPassingEmptyStringAsFilePath()
|
|
|
{
|
|
|
Assert.Throws<ArgumentException>(() => new ConfigurationBuilder().AddJsonFile(string.Empty));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// json文件不存在时,抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_Throws_On_Missing_Configuration_File()
|
|
|
{
|
|
|
var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: false);
|
|
|
var exception = Assert.Throws<FileNotFoundException>(() => config.Build());
|
|
|
|
|
|
// Assert
|
|
|
Assert.StartsWith($"The configuration file 'NotExistingConfig.json' was not found and is not optional. The expected physical path was '", exception.Message);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// json文件不存在时,不抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_Does_Not_Throw_On_Optional_Configuration()
|
|
|
{
|
|
|
var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: true).Build();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// json内容为空白时,抛出异常
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void ConfigurationSource_ThrowFormatExceptionWhenFileIsEmpty()
|
|
|
{
|
|
|
var exception = Assert.Throws<FormatException>(() => LoadProvider(@""));
|
|
|
Assert.Contains("Could not parse the JSON file.", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void UseJsonConfigurationSource_Test()
|
|
|
{
|
|
|
var root = new ConfigurationBuilder()
|
|
|
.AddEnvironmentVariables()
|
|
|
.AddJsonFile("Configs/appsettings.json",true,true)
|
|
|
.Build();
|
|
|
|
|
|
var option = root.Get<AppOption>();
|
|
|
|
|
|
Assert.NotNull(option);
|
|
|
}
|
|
|
|
|
|
#region 私有辅助方法
|
|
|
private Stream StringToStream(string str)
|
|
|
{
|
|
|
var memStream = new MemoryStream();
|
|
|
var textWriter = new StreamWriter(memStream);
|
|
|
textWriter.Write(str);
|
|
|
textWriter.Flush();
|
|
|
memStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
return memStream;
|
|
|
}
|
|
|
|
|
|
private JsonConfigurationProvider LoadProvider(string json)
|
|
|
{
|
|
|
var p = new JsonConfigurationProvider(new JsonConfigurationSource { Optional = true });
|
|
|
p.Load(StringToStream(json));
|
|
|
return p;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
public void Dispose()
|
|
|
{
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|