// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Claims; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using NetTopologySuite.IO.Converters; using NodaTime; using NodaTime.Serialization.SystemTextJson; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps.Json; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents.Json; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Json; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.System; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries.Json; using Squidex.Infrastructure.Reflection; #pragma warning disable SYSLIB0011 // Type or member is obsolete namespace Squidex.Domain.Apps.Core.TestHelpers; public static class TestUtils { public static readonly TypeRegistry TypeRegistry = CreateTypeRegistry(typeof(TestUtils).Assembly); public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); public sealed class ObjectHolder { [BsonRequired] public T Value1 { get; set; } [BsonRequired] public T Value2 { get; set; } } static TestUtils() { SetupBson(); } public static void SetupBson() { BsonDefaultConventions.Register(); BsonDomainIdSerializer.Register(); BsonEscapedDictionarySerializer.Register(); BsonEscapedDictionarySerializer.Register(); BsonEscapedDictionarySerializer.Register(); BsonInstantSerializer.Register(); BsonJsonConvention.Register(DefaultOptions()); BsonJsonValueSerializer.Register(); BsonStringSerializer.Register(); BsonStringSerializer.Register(); } public static TypeRegistry CreateTypeRegistry(Assembly assembly) { var typeRegistry = new TypeRegistry() .Map(new FieldTypeProvider()) .Map(new AssemblyTypeProvider(assembly)) .Map(new AssemblyTypeProvider(SquidexEvents.Assembly)) .Map(new AssemblyTypeProvider(assembly)) .Map(new AssemblyTypeProvider(assembly)) .Map(new RuleTypeProvider()); return typeRegistry; } public static IJsonSerializer CreateSerializer(Action? configure = null) { var serializerSettings = DefaultOptions(configure); return new SystemJsonSerializer(serializerSettings); } public static JsonSerializerOptions DefaultOptions(Action? configure = null) { var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); // It is also a readonly list, so we have to register it first, so that other converters do not pick this up. options.Converters.Add(new StringConverter(x => x)); options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); options.Converters.Add(new GeoJsonConverterFactory()); options.Converters.Add(new PolymorphicConverter(TypeRegistry)); options.Converters.Add(new PolymorphicConverter(TypeRegistry)); options.Converters.Add(new PolymorphicConverter(TypeRegistry)); options.Converters.Add(new PolymorphicConverter(TypeRegistry)); options.Converters.Add(new JsonValueConverter()); options.Converters.Add(new ReadonlyDictionaryConverterFactory()); options.Converters.Add(new ReadonlyListConverterFactory()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter, FieldsSurrogate>()); options.Converters.Add(new SurrogateJsonConverter, JsonFilterSurrogate>()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new SurrogateJsonConverter()); options.Converters.Add(new StringConverter()); options.Converters.Add(new StringConverter()); options.Converters.Add(new StringConverter>()); options.Converters.Add(new StringConverter>()); options.Converters.Add(new StringConverter>()); options.Converters.Add(new StringConverter>()); options.Converters.Add(new StringConverter()); options.Converters.Add(new StringConverter()); options.Converters.Add(new StringConverter()); options.Converters.Add(new JsonStringEnumConverter()); options.IncludeFields = true; options.TypeInfoResolver = new DefaultJsonTypeInfoResolver() .WithAddedModifier(PolymorphicConverter.Modifier(TypeRegistry)) .WithAddedModifier(JsonIgnoreReadonlyProperties.Modifier()) .WithAddedModifier(JsonRenameAttribute.Modifier); configure?.Invoke(options); return options; } public static T SerializeAndDeserializeBinary(this T source) { using (var stream = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, source!); stream.Position = 0; return (T)formatter.Deserialize(stream); } } public static T SerializeAndDeserializeBson(this T value) { return SerializeAndDeserializeBson(value); } public static TOut SerializeAndDeserializeBson(this TIn value) { using var stream = new MemoryStream(); using (var writer = new BsonBinaryWriter(stream)) { BsonSerializer.Serialize(writer, new ObjectHolder { Value1 = value, Value2 = value }); } stream.Position = 0; using (var reader = new BsonBinaryReader(stream)) { return BsonSerializer.Deserialize>(reader).Value1; } } public static T SerializeAndDeserialize(this T value) { return SerializeAndDeserialize(value); } public static TOut SerializeAndDeserialize(this TIn value) { var json = DefaultSerializer.Serialize(new ObjectHolder { Value1 = value, Value2 = value }); return DefaultSerializer.Deserialize>(json).Value1; } public static T Deserialize(string value) { var json = DefaultSerializer.Serialize(new ObjectHolder { Value1 = value, Value2 = value }); return DefaultSerializer.Deserialize>(json).Value1; } public static string SerializeWithoutNulls(this T value) { var options = DefaultOptions(options => { options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; }); return new SystemJsonSerializer(options).Serialize(value, true).CleanJson(); } public static string CleanJson(this string json) { var document = System.Text.Json.Nodes.JsonNode.Parse(json); static void Handle(System.Text.Json.Nodes.JsonNode? node) { if (node is System.Text.Json.Nodes.JsonArray array) { foreach (var item in array) { Handle(item); } } else if (node is System.Text.Json.Nodes.JsonObject obj) { var properties = obj.ToList(); foreach (var (key, _) in properties) { obj.Remove(key); } foreach (var (key, value) in properties.OrderBy(x => x.Key)) { Handle(value); obj.Add(key, value); } } } Handle(document?.Root); return DefaultSerializer.Serialize(document, true); } public static TEvent CreateEvent(Action? init = null) where TEvent : IEvent, new() { var actual = new TEvent(); if (actual is SquidexEvent squidexEvent) { squidexEvent.Actor = RefToken.Client("my-client"); } if (actual is AppEvent appEvent) { appEvent.AppId = NamedId.Of(DomainId.NewGuid(), "my-app"); } if (actual is SchemaEvent schemaEvent) { schemaEvent.SchemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); } init?.Invoke(actual); return actual; } }