mirror of https://github.com/Squidex/squidex.git
30 changed files with 182 additions and 1157 deletions
@ -1,155 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using MongoDB.Bson; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Infrastructure.MongoDb |
|||
{ |
|||
public static class BsonJsonConverter |
|||
{ |
|||
public static BsonDocument ToBson(this JObject source) |
|||
{ |
|||
var result = new BsonDocument(); |
|||
|
|||
foreach (var property in source) |
|||
{ |
|||
result.Add(property.Key.EscapeJson(), property.Value.ToBson()); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static JObject ToJson(this BsonDocument source) |
|||
{ |
|||
var result = new JObject(); |
|||
|
|||
foreach (var property in source) |
|||
{ |
|||
result.Add(property.Name.UnescapeBson(), property.Value.ToJson()); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static BsonArray ToBson(this JArray source) |
|||
{ |
|||
var result = new BsonArray(); |
|||
|
|||
foreach (var item in source) |
|||
{ |
|||
result.Add(item.ToBson()); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static JArray ToJson(this BsonArray source) |
|||
{ |
|||
var result = new JArray(); |
|||
|
|||
foreach (var item in source) |
|||
{ |
|||
result.Add(item.ToJson()); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static BsonValue ToBson(this JToken source) |
|||
{ |
|||
switch (source.Type) |
|||
{ |
|||
case JTokenType.Object: |
|||
return ((JObject)source).ToBson(); |
|||
case JTokenType.Array: |
|||
return ((JArray)source).ToBson(); |
|||
case JTokenType.Integer: |
|||
return BsonValue.Create(((JValue)source).Value); |
|||
case JTokenType.Float: |
|||
return BsonValue.Create(((JValue)source).Value); |
|||
case JTokenType.String: |
|||
return BsonValue.Create(((JValue)source).Value); |
|||
case JTokenType.Boolean: |
|||
return BsonValue.Create(((JValue)source).Value); |
|||
case JTokenType.Null: |
|||
return BsonNull.Value; |
|||
case JTokenType.Undefined: |
|||
return BsonUndefined.Value; |
|||
case JTokenType.Bytes: |
|||
return BsonValue.Create(((JValue)source).Value); |
|||
case JTokenType.Guid: |
|||
return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); |
|||
case JTokenType.Uri: |
|||
return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); |
|||
case JTokenType.TimeSpan: |
|||
return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); |
|||
case JTokenType.Date: |
|||
{ |
|||
var value = ((JValue)source).Value; |
|||
|
|||
if (value is DateTime dateTime) |
|||
{ |
|||
return dateTime.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); |
|||
} |
|||
else if (value is DateTimeOffset dateTimeOffset) |
|||
{ |
|||
if (dateTimeOffset.Offset == TimeSpan.Zero) |
|||
{ |
|||
return dateTimeOffset.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); |
|||
} |
|||
else |
|||
{ |
|||
return dateTimeOffset.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return value.ToString(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new NotSupportedException($"Cannot convert {source.GetType()} to Bson."); |
|||
} |
|||
|
|||
public static JToken ToJson(this BsonValue source) |
|||
{ |
|||
switch (source.BsonType) |
|||
{ |
|||
case BsonType.Document: |
|||
return source.AsBsonDocument.ToJson(); |
|||
case BsonType.Array: |
|||
return source.AsBsonArray.ToJson(); |
|||
case BsonType.Double: |
|||
return new JValue(source.AsDouble); |
|||
case BsonType.String: |
|||
return new JValue(source.AsString); |
|||
case BsonType.Boolean: |
|||
return new JValue(source.AsBoolean); |
|||
case BsonType.DateTime: |
|||
return new JValue(source.ToUniversalTime()); |
|||
case BsonType.Int32: |
|||
return new JValue(source.AsInt32); |
|||
case BsonType.Int64: |
|||
return new JValue(source.AsInt64); |
|||
case BsonType.Decimal128: |
|||
return new JValue(source.AsDecimal); |
|||
case BsonType.Binary: |
|||
return new JValue(source.AsBsonBinaryData.Bytes); |
|||
case BsonType.Null: |
|||
return JValue.CreateNull(); |
|||
case BsonType.Undefined: |
|||
return JValue.CreateUndefined(); |
|||
} |
|||
|
|||
throw new NotSupportedException($"Cannot convert {source.GetType()} to Json."); |
|||
} |
|||
} |
|||
} |
|||
@ -1,78 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Newtonsoft.Json; |
|||
using NodaTime; |
|||
using NodaTime.Extensions; |
|||
|
|||
namespace Squidex.Infrastructure.Json.Newtonsoft |
|||
{ |
|||
public sealed class PropertiesBagConverter<T> : JsonClassConverter<T> where T : PropertiesBag, new() |
|||
{ |
|||
protected override void WriteValue(JsonWriter writer, T value, JsonSerializer serializer) |
|||
{ |
|||
writer.WriteStartObject(); |
|||
|
|||
foreach (var kvp in value.Properties) |
|||
{ |
|||
writer.WritePropertyName(kvp.Key); |
|||
|
|||
if (kvp.Value.RawValue is Instant instant) |
|||
{ |
|||
writer.WriteValue(instant.ToString()); |
|||
} |
|||
else |
|||
{ |
|||
writer.WriteValue(kvp.Value.RawValue); |
|||
} |
|||
} |
|||
|
|||
writer.WriteEndObject(); |
|||
} |
|||
|
|||
protected override T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) |
|||
{ |
|||
if (reader.TokenType != JsonToken.StartObject) |
|||
{ |
|||
throw new JsonException($"Expected Object, but got {reader.TokenType}."); |
|||
} |
|||
|
|||
var properties = new T(); |
|||
|
|||
while (reader.Read()) |
|||
{ |
|||
if (reader.TokenType != JsonToken.PropertyName) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
var key = reader.Value.ToString(); |
|||
|
|||
reader.Read(); |
|||
|
|||
var value = reader.Value; |
|||
|
|||
if (value is DateTime dateTime) |
|||
{ |
|||
properties.Set(key, dateTime.ToInstant()); |
|||
} |
|||
else |
|||
{ |
|||
properties.Set(key, value); |
|||
} |
|||
} |
|||
|
|||
return properties; |
|||
} |
|||
|
|||
public override bool CanConvert(Type objectType) |
|||
{ |
|||
return objectType == typeof(T); |
|||
} |
|||
} |
|||
} |
|||
@ -1,112 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Dynamic; |
|||
|
|||
namespace Squidex.Infrastructure |
|||
{ |
|||
public class PropertiesBag : DynamicObject |
|||
{ |
|||
private static readonly PropertyValue FallbackValue = new PropertyValue(null); |
|||
private readonly Dictionary<string, PropertyValue> internalDictionary = new Dictionary<string, PropertyValue>(StringComparer.OrdinalIgnoreCase); |
|||
|
|||
public int Count |
|||
{ |
|||
get { return internalDictionary.Count; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<string, PropertyValue> Properties |
|||
{ |
|||
get { return internalDictionary; } |
|||
} |
|||
|
|||
public IEnumerable<string> PropertyNames |
|||
{ |
|||
get { return internalDictionary.Keys; } |
|||
} |
|||
|
|||
public PropertyValue this[string propertyName] |
|||
{ |
|||
get |
|||
{ |
|||
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
|||
|
|||
return internalDictionary.GetOrDefault(propertyName) ?? FallbackValue; |
|||
} |
|||
} |
|||
|
|||
public override IEnumerable<string> GetDynamicMemberNames() |
|||
{ |
|||
return internalDictionary.Keys; |
|||
} |
|||
|
|||
public override bool TryGetMember(GetMemberBinder binder, out object result) |
|||
{ |
|||
result = this[binder.Name]; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public override bool TrySetMember(SetMemberBinder binder, object value) |
|||
{ |
|||
internalDictionary[binder.Name] = new PropertyValue(value); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public bool Contains(string propertyName) |
|||
{ |
|||
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
|||
|
|||
return internalDictionary.ContainsKey(propertyName); |
|||
} |
|||
|
|||
public bool Remove(string propertyName) |
|||
{ |
|||
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
|||
|
|||
return internalDictionary.Remove(propertyName); |
|||
} |
|||
|
|||
public PropertiesBag Set(string propertyName, object value) |
|||
{ |
|||
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); |
|||
|
|||
internalDictionary[propertyName] = new PropertyValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public bool Rename(string oldPropertyName, string newPropertyName) |
|||
{ |
|||
Guard.NotNullOrEmpty(oldPropertyName, nameof(oldPropertyName)); |
|||
Guard.NotNullOrEmpty(newPropertyName, nameof(newPropertyName)); |
|||
|
|||
if (internalDictionary.ContainsKey(newPropertyName)) |
|||
{ |
|||
throw new ArgumentException($"An property with the key '{newPropertyName}' already exists.", newPropertyName); |
|||
} |
|||
|
|||
if (string.Equals(oldPropertyName, newPropertyName, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); |
|||
} |
|||
|
|||
if (!internalDictionary.TryGetValue(oldPropertyName, out var property)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
internalDictionary[newPropertyName] = property; |
|||
internalDictionary.Remove(oldPropertyName); |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -1,235 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Dynamic; |
|||
using System.Globalization; |
|||
using NodaTime; |
|||
using NodaTime.Text; |
|||
|
|||
namespace Squidex.Infrastructure |
|||
{ |
|||
public sealed class PropertyValue : DynamicObject |
|||
{ |
|||
private readonly object rawValue; |
|||
|
|||
private static readonly Dictionary<Type, Func<PropertyValue, object>> Parsers = |
|||
new Dictionary<Type, Func<PropertyValue, object>> |
|||
{ |
|||
{ typeof(string), p => p.ToString() }, |
|||
{ typeof(bool), p => p.ToBoolean() }, |
|||
{ typeof(bool?), p => p.ToNullableBoolean() }, |
|||
{ typeof(float), p => p.ToSingle() }, |
|||
{ typeof(float?), p => p.ToNullableSingle() }, |
|||
{ typeof(double), p => p.ToDouble() }, |
|||
{ typeof(double?), p => p.ToNullableDouble() }, |
|||
{ typeof(int), p => p.ToInt32() }, |
|||
{ typeof(int?), p => p.ToNullableInt32() }, |
|||
{ typeof(long), p => p.ToInt64() }, |
|||
{ typeof(long?), p => p.ToNullableInt64() }, |
|||
{ typeof(Instant), p => p.ToInstant() }, |
|||
{ typeof(Instant?), p => p.ToNullableInstant() }, |
|||
{ typeof(Guid), p => p.ToGuid() }, |
|||
{ typeof(Guid?), p => p.ToNullableGuid() } |
|||
}; |
|||
|
|||
public object RawValue |
|||
{ |
|||
get { return rawValue; } |
|||
} |
|||
|
|||
internal PropertyValue(object rawValue) |
|||
{ |
|||
if (rawValue != null && !Parsers.ContainsKey(rawValue.GetType())) |
|||
{ |
|||
throw new InvalidOperationException($"The type '{rawValue.GetType()}' is not supported."); |
|||
} |
|||
|
|||
this.rawValue = rawValue; |
|||
} |
|||
|
|||
public override bool TryConvert(ConvertBinder binder, out object result) |
|||
{ |
|||
result = null; |
|||
|
|||
if (!Parsers.TryGetValue(binder.Type, out var parser)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
result = parser(this); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return rawValue?.ToString(); |
|||
} |
|||
|
|||
public bool ToBoolean() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); |
|||
} |
|||
|
|||
public bool? ToNullableBoolean() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); |
|||
} |
|||
|
|||
public float ToSingle() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public float? ToNullableSingle() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public double ToDouble() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public double? ToNullableDouble() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public int ToInt32() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public int? ToNullableInt32() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public long ToInt64() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public long? ToNullableInt64() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
public Instant ToInstant() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); |
|||
} |
|||
|
|||
public Instant? ToNullableInstant() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); |
|||
} |
|||
|
|||
public Guid ToGuid() |
|||
{ |
|||
return ToOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); |
|||
} |
|||
|
|||
public Guid? ToNullableGuid() |
|||
{ |
|||
return ToNullableOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); |
|||
} |
|||
|
|||
private T? ToNullableOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) where T : struct |
|||
{ |
|||
return TryParse(culture, parser, out var result) ? result : (T?)null; |
|||
} |
|||
|
|||
private T ToOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) |
|||
{ |
|||
return TryParse(culture, parser, out var result) ? result : default(T); |
|||
} |
|||
|
|||
private bool TryParse<T>(IFormatProvider culture, Func<string, T> parser, out T result) |
|||
{ |
|||
var value = rawValue; |
|||
|
|||
if (value != null) |
|||
{ |
|||
var valueType = value.GetType(); |
|||
|
|||
if (valueType == typeof(T)) |
|||
{ |
|||
result = (T)value; |
|||
} |
|||
else if (valueType == typeof(string)) |
|||
{ |
|||
result = Parse(parser, valueType, value); |
|||
} |
|||
else |
|||
{ |
|||
result = Convert<T>(culture, value, valueType); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
result = default(T); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static T Convert<T>(IFormatProvider culture, object value, Type valueType) |
|||
{ |
|||
var requestedType = typeof(T); |
|||
|
|||
try |
|||
{ |
|||
return (T)System.Convert.ChangeType(value, requestedType, culture); |
|||
} |
|||
catch (OverflowException) |
|||
{ |
|||
var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}' because it is either too small or large."; |
|||
|
|||
throw new InvalidCastException(message); |
|||
} |
|||
catch (InvalidCastException) |
|||
{ |
|||
var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; |
|||
|
|||
throw new InvalidCastException(message); |
|||
} |
|||
} |
|||
|
|||
private static T Parse<T>(Func<string, T> parser, Type valueType, object value) |
|||
{ |
|||
var requestedType = typeof(T); |
|||
|
|||
try |
|||
{ |
|||
return parser(value.ToString()); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; |
|||
|
|||
throw new InvalidCastException(message, ex); |
|||
} |
|||
} |
|||
|
|||
private static bool ParseBoolean(string value) |
|||
{ |
|||
switch (value) |
|||
{ |
|||
case "1": |
|||
return true; |
|||
case "0": |
|||
return false; |
|||
default: |
|||
return bool.Parse(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,428 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright () Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using Microsoft.CSharp.RuntimeBinder; |
|||
using NodaTime; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure |
|||
{ |
|||
public class PropertiesBagTests |
|||
{ |
|||
private readonly PropertiesBag bag = new PropertiesBag(); |
|||
private readonly dynamic dynamicBag; |
|||
|
|||
public PropertiesBagTests() |
|||
{ |
|||
dynamicBag = bag; |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_serialize_and_deserialize_empty_bag() |
|||
{ |
|||
var output = bag.SerializeAndDeserialize(); |
|||
|
|||
Assert.Equal(bag.Count, output.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_serialize_and_deserialize() |
|||
{ |
|||
var time = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); |
|||
|
|||
bag.Set("Key1", time); |
|||
bag.Set("Key2", "MyString"); |
|||
bag.Set("Key3", 123L); |
|||
bag.Set("Key4", true); |
|||
bag.Set("Key5", Guid.NewGuid()); |
|||
|
|||
var output = bag.SerializeAndDeserialize(); |
|||
|
|||
foreach (var kvp in output.Properties.Take(4)) |
|||
{ |
|||
Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue); |
|||
} |
|||
|
|||
Assert.Equal(bag["Key5"].ToGuid(), output["Key5"].ToGuid()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_false_when_renaming_unknown_property() |
|||
{ |
|||
Assert.False(bag.Rename("OldKey", "NewKey")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_renaming_to_existing_property() |
|||
{ |
|||
bag.Set("NewKey", 1); |
|||
|
|||
Assert.Throws<ArgumentException>(() => bag.Rename("OldKey", "NewKey")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_renaming_to_same_key() |
|||
{ |
|||
Assert.Throws<ArgumentException>(() => bag.Rename("SameKey", "SameKey")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_provide_property_with_new_name_after_rename() |
|||
{ |
|||
bag.Set("OldKey", 123); |
|||
|
|||
Assert.True(bag.Rename("OldKey", "NewKey")); |
|||
Assert.True(bag.Contains("NewKey")); |
|||
|
|||
Assert.Equal(1, bag.Count); |
|||
Assert.Equal(123, bag["NewKey"].ToInt64()); |
|||
|
|||
Assert.False(bag.Contains("OldKey")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_calculate_count_correctly() |
|||
{ |
|||
bag.Set("Key1", 1); |
|||
bag.Set("Key2", 1); |
|||
|
|||
Assert.Equal(2, bag.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_calculate_keys_correctly() |
|||
{ |
|||
bag.Set("Key1", 1); |
|||
bag.Set("Key2", 1); |
|||
|
|||
Assert.Equal(new[] { "Key1", "Key2" }, bag.PropertyNames.ToArray()); |
|||
Assert.Equal(new[] { "Key1", "Key2" }, bag.Properties.Keys.ToArray()); |
|||
Assert.Equal(new[] { "Key1", "Key2" }, bag.GetDynamicMemberNames().ToArray()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_correct_value_when_contains_check() |
|||
{ |
|||
Assert.False(bag.Contains("Key")); |
|||
|
|||
bag.Set("Key", 1); |
|||
|
|||
Assert.True(bag.Contains("Key")); |
|||
Assert.True(bag.Contains("KEY")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_returne_false_when_property_to_rename_does_not_exist() |
|||
{ |
|||
Assert.False(bag.Remove("NOTFOUND")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_ignore_casing_when_returning() |
|||
{ |
|||
bag.Set("Key", 1); |
|||
|
|||
Assert.True(bag.Remove("KEY")); |
|||
Assert.False(bag.Contains("KEY")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_set_value_as_dynamic() |
|||
{ |
|||
dynamicBag.Key = 456; |
|||
|
|||
Assert.Equal(456, (int)dynamicBag.Key); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_setting_value_with_invalid_type_dynamically() |
|||
{ |
|||
Assert.Throws<InvalidOperationException>(() => dynamicBag.Key = (byte)123); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_setting_value_with_invalid_type() |
|||
{ |
|||
Assert.Throws<InvalidOperationException>(() => bag.Set("Key", (byte)1)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_false_when_making_contains_check() |
|||
{ |
|||
Assert.False(dynamicBag.Contains("Key")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_provide_default_value_if_not_exists() |
|||
{ |
|||
Assert.Equal(0, (int)dynamicBag.Key); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_parsing_failed() |
|||
{ |
|||
bag.Set("Key", "abc"); |
|||
|
|||
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt64()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_false_when_converter_does_not_exist() |
|||
{ |
|||
bag.Set("Key", "abc"); |
|||
|
|||
Assert.Throws<RuntimeBinderException>(() => (TimeSpan)dynamicBag.Key); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_string_to_numbers() |
|||
{ |
|||
bag.Set("Key", 123); |
|||
|
|||
AssertNumber(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_int_to_numbers() |
|||
{ |
|||
bag.Set("Key", 123); |
|||
|
|||
AssertNumber(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_long_to_numbers() |
|||
{ |
|||
bag.Set("Key", 123L); |
|||
|
|||
AssertNumber(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_casting_from_large_long() |
|||
{ |
|||
bag.Set("Key", long.MaxValue); |
|||
|
|||
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt32()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_float_to_number() |
|||
{ |
|||
bag.Set("Key", 123f); |
|||
|
|||
AssertNumber(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_double_to_number() |
|||
{ |
|||
bag.Set("Key", 123d); |
|||
|
|||
AssertNumber(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_casting_from_large_doule() |
|||
{ |
|||
bag.Set("Key", double.MaxValue); |
|||
|
|||
Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_instant_value() |
|||
{ |
|||
var time = SystemClock.Instance.GetCurrentInstant(); |
|||
|
|||
bag.Set("Key", time); |
|||
|
|||
AssertInstant(time); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_instant_string() |
|||
{ |
|||
var time = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); |
|||
|
|||
bag.Set("Key", time.ToString()); |
|||
|
|||
AssertInstant(time); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_guid_value() |
|||
{ |
|||
var id = Guid.NewGuid(); |
|||
|
|||
bag.Set("Key", id); |
|||
|
|||
AssertGuid(id); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_guid_string() |
|||
{ |
|||
var id = Guid.NewGuid(); |
|||
|
|||
bag.Set("Key", id.ToString()); |
|||
|
|||
AssertGuid(id); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_boolean_value() |
|||
{ |
|||
bag.Set("Key", true); |
|||
|
|||
AssertBoolean(true); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_from_boolean_string() |
|||
{ |
|||
bag.Set("Key", "true"); |
|||
|
|||
AssertBoolean(true); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_boolean_from_number() |
|||
{ |
|||
bag.Set("Key", 1); |
|||
|
|||
AssertBoolean(true); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_boolean_to_truthy_number_string() |
|||
{ |
|||
bag.Set("Key", "1"); |
|||
|
|||
AssertBoolean(true); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_convert_boolean_to_falsy_number_string() |
|||
{ |
|||
bag.Set("Key", "0"); |
|||
|
|||
AssertBoolean(false); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_provide_value_as_string() |
|||
{ |
|||
bag.Set("Key", "Foo"); |
|||
|
|||
AssertString("Foo"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_provide_null() |
|||
{ |
|||
bag.Set("Key", null); |
|||
|
|||
AssertNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_throw_when_converting_instant_to_number() |
|||
{ |
|||
bag.Set("Key", SystemClock.Instance.GetCurrentInstant()); |
|||
|
|||
Assert.Throws<InvalidCastException>(() => bag["Key"].ToGuid()); |
|||
} |
|||
|
|||
private void AssertNumber() |
|||
{ |
|||
AssertInt32(123); |
|||
AssertInt64(123); |
|||
AssertSingle(123); |
|||
AssertDouble(123); |
|||
} |
|||
|
|||
private void AssertString(string expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToString()); |
|||
|
|||
Assert.Equal(expected, (string)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertNull() |
|||
{ |
|||
Assert.Null(bag["Key"].ToString()); |
|||
Assert.Null(bag["Key"].RawValue); |
|||
} |
|||
|
|||
private void AssertBoolean(bool expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToBoolean()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableBoolean()); |
|||
|
|||
Assert.Equal(expected, (bool)dynamicBag.Key); |
|||
Assert.Equal(expected, (bool?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertInstant(Instant expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToInstant()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableInstant().Value); |
|||
|
|||
Assert.Equal(expected, (Instant)dynamicBag.Key); |
|||
Assert.Equal(expected, (Instant?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertGuid(Guid expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToGuid()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableGuid()); |
|||
|
|||
Assert.Equal(expected, (Guid)dynamicBag.Key); |
|||
Assert.Equal(expected, (Guid?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertDouble(double expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToDouble()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableDouble()); |
|||
|
|||
Assert.Equal(expected, (double)dynamicBag.Key); |
|||
Assert.Equal(expected, (double?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertSingle(float expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToSingle()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableSingle()); |
|||
|
|||
Assert.Equal(expected, (float)dynamicBag.Key); |
|||
Assert.Equal(expected, (float?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertInt32(long expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToInt64()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableInt64()); |
|||
|
|||
Assert.Equal(expected, (long)dynamicBag.Key); |
|||
Assert.Equal(expected, (long?)dynamicBag.Key); |
|||
} |
|||
|
|||
private void AssertInt64(int expected) |
|||
{ |
|||
Assert.Equal(expected, bag["Key"].ToInt64()); |
|||
Assert.Equal(expected, bag["Key"].ToNullableInt64()); |
|||
|
|||
Assert.Equal(expected, (int)dynamicBag.Key); |
|||
Assert.Equal(expected, (int?)dynamicBag.Key); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue