You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

386 lines
13 KiB
C#

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()
{
}
}
}