Browse Source

Fix JSON dot. (#911)

* Fix JSON dot.

* Serializer fix.

* Fix queries.
pull/912/head
Sebastian Stehle 3 years ago
committed by GitHub
parent
commit
93a667a0b0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/UniqueValidator.cs
  2. 1
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs
  3. 1
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs
  4. 1
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs
  5. 4
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Adapt.cs
  7. 1
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryAsStream.cs
  8. 1
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs
  9. 1
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SchemaUpdateCommand.cs
  10. 65
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonEscapedDictionarySerializer.cs
  11. 12
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonHelper.cs
  12. 1
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs
  13. 6
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonSerializer.cs
  14. 2
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoBase.cs
  15. 1
      backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStoreBase.cs
  16. 1
      backend/src/Squidex.Infrastructure/Json/System/InheritanceConverter.cs
  17. 3
      backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs
  18. 55
      backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs
  19. 1
      backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs
  20. 1
      backend/src/Squidex/Config/Web/WebServices.cs
  21. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs
  22. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs
  23. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs
  24. 34
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs
  25. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetQueryTests.cs
  26. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryFixture.cs
  27. 32
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentQueryTests.cs
  28. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs
  29. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/StatusSerializerTests.cs
  30. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/AtlasTextIndexFixture.cs
  31. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/MongoTextIndexFixture.cs
  32. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/MongoDb/SchemasHashFixture.cs
  33. 1
      backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventFormatterTests.cs
  34. 24
      backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs
  35. 1
      backend/tests/Squidex.Infrastructure.Tests/Json/System/JsonInheritanceConverterBaseTests.cs
  36. 3
      backend/tests/Squidex.Infrastructure.Tests/MongoDb/DomainIdSerializerTests.cs
  37. 2
      backend/tests/Squidex.Infrastructure.Tests/MongoDb/InstantSerializerTests.cs
  38. 7
      backend/tests/Squidex.Infrastructure.Tests/MongoDb/MongoQueryTests.cs
  39. 4
      backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs
  40. 102
      backend/tests/Squidex.Infrastructure.Tests/Queries/PropertyPathTests.cs
  41. 30
      backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs
  42. 5
      backend/tools/TestSuite/TestSuite.ApiTests/BackupTests.cs
  43. 23
      backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs
  44. 49
      backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  45. 70
      backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs
  46. 15
      backend/tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs
  47. 24
      backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.Should_create_json_with_dot.verified.txt
  48. 262
      backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  49. 5
      backend/tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs
  50. 5
      backend/tools/TestSuite/TestSuite.LoadTests/WritingBenchmarks.cs

1
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/UniqueValidator.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;

1
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets.DomainObject; using Squidex.Domain.Apps.Entities.Assets.DomainObject;

1
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets.DomainObject; using Squidex.Domain.Apps.Entities.Assets.DomainObject;

1
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;

4
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Hosting; using Squidex.Hosting;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
@ -32,6 +33,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
static MongoContentRepository() static MongoContentRepository()
{ {
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonEscapedDictionarySerializer<JsonValue, ContentFieldData>.Register();
BsonEscapedDictionarySerializer<ContentFieldData, ContentData>.Register();
BsonStringSerializer<Status>.Register(); BsonStringSerializer<Status>.Register();
} }

2
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Adapt.cs

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
for (var i = 1; i < path.Count; i++) for (var i = 1; i < path.Count; i++)
{ {
result[i] = result[i].UnescapeEdmField().JsonEscape(); result[i] = result[i].UnescapeEdmField().JsonToBsonName().JsonEscape();
} }
return result; return result;

1
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryAsStream.cs

@ -7,7 +7,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure; using Squidex.Infrastructure;

1
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;

1
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SchemaUpdateCommand.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Runtime.Serialization;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands namespace Squidex.Domain.Apps.Entities.Schemas.Commands

65
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonEscapedDictionarySerializer.cs

@ -0,0 +1,65 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace Squidex.Infrastructure.MongoDb
{
public sealed class BsonEscapedDictionarySerializer<TValue, TInstance> : ClassSerializerBase<TInstance> where TInstance : Dictionary<string, TValue?>, new()
{
public static void Register()
{
try
{
BsonSerializer.RegisterSerializer(new BsonEscapedDictionarySerializer<TValue, TInstance>());
}
catch (BsonSerializationException)
{
return;
}
}
protected override TInstance DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var reader = context.Reader;
var result = new TInstance();
reader.ReadStartDocument();
while (reader.ReadBsonType() != BsonType.EndOfDocument)
{
var key = reader.ReadName().BsonToJsonName();
result.Add(key, BsonSerializer.Deserialize<TValue>(reader));
}
reader.ReadEndDocument();
return result;
}
protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, TInstance value)
{
var writer = context.Writer;
writer.WriteStartDocument();
foreach (var property in value)
{
writer.WriteName(property.Key.JsonToBsonName());
BsonSerializer.Serialize(writer, property.Value);
}
writer.WriteEndDocument();
}
}
}

12
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonHelper.cs

@ -12,10 +12,10 @@ namespace Squidex.Infrastructure.MongoDb
private const string Empty = "§empty"; private const string Empty = "§empty";
private const string TypeBson = "§type"; private const string TypeBson = "§type";
private const string TypeJson = "$type"; private const string TypeJson = "$type";
private const string DotSource = "."; private const string DotBson = "_§§_";
private const string DotReplacement = "_§§_"; private const string DotJson = ".";
public static string UnescapeBson(this string value) public static string BsonToJsonName(this string value)
{ {
if (value == Empty) if (value == Empty)
{ {
@ -27,12 +27,12 @@ namespace Squidex.Infrastructure.MongoDb
return TypeJson; return TypeJson;
} }
var result = value.ReplaceFirst('§', '$').Replace(DotReplacement, DotSource, StringComparison.Ordinal); var result = value.ReplaceFirst('§', '$').Replace(DotBson, DotJson, StringComparison.Ordinal);
return result; return result;
} }
public static string EscapeJson(this string value) public static string JsonToBsonName(this string value)
{ {
if (value.Length == 0) if (value.Length == 0)
{ {
@ -44,7 +44,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeBson; return TypeBson;
} }
var result = value.ReplaceFirst('$', '§').Replace(DotSource, DotReplacement, StringComparison.Ordinal); var result = value.ReplaceFirst('$', '§').Replace(DotJson, DotBson, StringComparison.Ordinal);
return result; return result;
} }

1
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
using System.Text.Json; using System.Text.Json;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;

6
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonSerializer.cs

@ -14,7 +14,7 @@ using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Infrastructure.MongoDb namespace Squidex.Infrastructure.MongoDb
{ {
public sealed class BsonJsonSerializer<T> : ClassSerializerBase<T?>, IRepresentationConfigurable<BsonJsonSerializer<T>> where T : class public sealed class BsonJsonSerializer<T> : SerializerBase<T?>, IRepresentationConfigurable<BsonJsonSerializer<T>> where T : class
{ {
public BsonType Representation { get; } public BsonType Representation { get; }
@ -134,7 +134,7 @@ namespace Squidex.Infrastructure.MongoDb
Read(); Read();
break; break;
case BsonReaderState.Name: case BsonReaderState.Name:
writer.WritePropertyName(reader.ReadName().UnescapeBson()); writer.WritePropertyName(reader.ReadName().BsonToJsonName());
Read(); Read();
break; break;
case BsonReaderState.Value: case BsonReaderState.Value:
@ -247,7 +247,7 @@ namespace Squidex.Infrastructure.MongoDb
foreach (var property in element.EnumerateObject()) foreach (var property in element.EnumerateObject())
{ {
writer.WriteName(property.Name.EscapeJson()); writer.WriteName(property.Name.JsonToBsonName());
WriteElement(writer, property.Value); WriteElement(writer, property.Value);
} }

2
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoBase.cs

@ -7,6 +7,7 @@
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable RECS0108 // Warns about static fields in generic types #pragma warning disable RECS0108 // Warns about static fields in generic types
@ -48,6 +49,7 @@ namespace Squidex.Infrastructure.MongoDb
{ {
BsonDefaultConventions.Register(); BsonDefaultConventions.Register();
BsonDomainIdSerializer.Register(); BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonInstantSerializer.Register(); BsonInstantSerializer.Register();
BsonJsonConvention.Register(); BsonJsonConvention.Register();
BsonJsonValueSerializer.Register(); BsonJsonValueSerializer.Register();

1
backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStoreBase.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;

1
backend/src/Squidex.Infrastructure/Json/System/InheritanceConverter.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text.Json;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Infrastructure.Json.System namespace Squidex.Infrastructure.Json.System

3
backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs

@ -5,9 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;

55
backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs

@ -24,7 +24,53 @@ namespace Squidex.Infrastructure.Queries
public static implicit operator PropertyPath(string path) public static implicit operator PropertyPath(string path)
{ {
return Create(path?.Split(Separators, StringSplitOptions.RemoveEmptyEntries).ToList()); var result = new List<string>();
var currentPath = path.AsSpan();
var currentPosition = 0;
void Add(ReadOnlySpan<char> value)
{
var property = value.Trim(Separators).ToString();
if (property.Length == 0)
{
return;
}
property = property.Replace("\\/", "/", StringComparison.OrdinalIgnoreCase);
property = property.Replace("\\.", ".", StringComparison.OrdinalIgnoreCase);
result.Add(property);
}
while (true)
{
var nextDot = currentPath[currentPosition..].IndexOfAny(Separators) + currentPosition;
if (nextDot < currentPosition)
{
Add(currentPath);
break;
}
else if (nextDot == currentPosition)
{
currentPath = currentPath[1..];
}
else if (currentPath[nextDot - 1] == '\\')
{
currentPosition = nextDot + 1;
}
else
{
Add(currentPath[..nextDot]);
currentPath = currentPath[nextDot..].Trim(Separators);
currentPosition = 0;
}
}
return Create(result);
} }
public static implicit operator PropertyPath(string[] path) public static implicit operator PropertyPath(string[] path)
@ -42,6 +88,13 @@ namespace Squidex.Infrastructure.Queries
return string.Join(".", this); return string.Join(".", this);
} }
private static string Unescape(string source)
{
return source
.Replace("\\/", "/", StringComparison.OrdinalIgnoreCase)
.Replace("\\.", ".", StringComparison.OrdinalIgnoreCase);
}
private static PropertyPath Create(IEnumerable<string>? source) private static PropertyPath Create(IEnumerable<string>? source)
{ {
var inner = source?.ToList(); var inner = source?.ToList();

1
backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Squidex.Assets; using Squidex.Assets;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;

1
backend/src/Squidex/Config/Web/WebServices.cs

@ -8,7 +8,6 @@
using GraphQL; using GraphQL;
using GraphQL.DataLoader; using GraphQL.DataLoader;
using GraphQL.DI; using GraphQL.DI;
using GraphQL.Execution;
using GraphQL.MicrosoftDI; using GraphQL.MicrosoftDI;
using GraphQL.Server.Transports.AspNetCore; using GraphQL.Server.Transports.AspNetCore;
using GraphQL.SystemTextJson; using GraphQL.SystemTextJson;

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs

@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
"Permission1", "Permission1",
"Permission2")); "Permission2"));
var roles = source.SerializeAndDeserialize<Roles>(); var roles = source.SerializeAndDeserialize<Roles, Dictionary<string, string[]>>();
roles.Should().BeEquivalentTo(expected); roles.Should().BeEquivalentTo(expected);
} }

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
{ {
var jsonStep = new { noUpdate = true }; var jsonStep = new { noUpdate = true };
var serialized = jsonStep.SerializeAndDeserialize<WorkflowStep>(); var serialized = jsonStep.SerializeAndDeserialize<WorkflowStep, object>();
Assert.Equal(new WorkflowStep(null, null, NoUpdate.Always), serialized); Assert.Equal(new WorkflowStep(null, null, NoUpdate.Always), serialized);
} }

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs

@ -487,7 +487,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
new Schema("my-schema", type: SchemaType.Singleton) new Schema("my-schema", type: SchemaType.Singleton)
.Publish(); .Publish();
var schemaTarget = schemaSource.SerializeAndDeserialize<Schema>(); var schemaTarget = schemaSource.SerializeAndDeserialize<Schema, object>();
schemaTarget.Should().BeEquivalentTo(expected); schemaTarget.Should().BeEquivalentTo(expected);
} }

34
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs

@ -59,9 +59,15 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
public static void SetupBson() public static void SetupBson()
{ {
BsonDomainIdSerializer.Register(); BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<ContentFieldData, ContentData>.Register();
BsonEscapedDictionarySerializer<JsonValue, ContentFieldData>.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonInstantSerializer.Register(); BsonInstantSerializer.Register();
BsonJsonConvention.Register(DefaultOptions()); BsonJsonConvention.Register(DefaultOptions());
BsonJsonValueSerializer.Register(); BsonJsonValueSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
} }
public static IJsonSerializer CreateSerializer(Action<JsonSerializerOptions>? configure = null) public static IJsonSerializer CreateSerializer(Action<JsonSerializerOptions>? configure = null)
@ -131,34 +137,37 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
} }
public static T SerializeAndDeserializeBson<T>(this T value) public static T SerializeAndDeserializeBson<T>(this T value)
{
return SerializeAndDeserializeBson<T, T>(value);
}
public static TOut SerializeAndDeserializeBson<TOut, TIn>(this TIn value)
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using (var writer = new BsonBinaryWriter(stream)) using (var writer = new BsonBinaryWriter(stream))
{ {
BsonSerializer.Serialize(writer, new ObjectHolder<T> { Value1 = value, Value2 = value }); BsonSerializer.Serialize(writer, new ObjectHolder<TIn> { Value1 = value, Value2 = value });
} }
stream.Position = 0; stream.Position = 0;
using (var reader = new BsonBinaryReader(stream)) using (var reader = new BsonBinaryReader(stream))
{ {
return BsonSerializer.Deserialize<ObjectHolder<T>>(reader).Value1; return BsonSerializer.Deserialize<ObjectHolder<TOut>>(reader).Value1;
} }
} }
public static T SerializeAndDeserialize<T>(this object value) public static T SerializeAndDeserialize<T>(this T value)
{ {
var json = DefaultSerializer.Serialize(value); return SerializeAndDeserialize<T, T>(value);
return DefaultSerializer.Deserialize<T>(json);
} }
public static T SerializeAndDeserialize<T>(this T value) public static TOut SerializeAndDeserialize<TOut, TIn>(this TIn value)
{ {
var json = DefaultSerializer.Serialize(new ObjectHolder<T> { Value1 = value, Value2 = value }); var json = DefaultSerializer.Serialize(new ObjectHolder<TIn> { Value1 = value, Value2 = value });
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1; return DefaultSerializer.Deserialize<ObjectHolder<TOut>>(json).Value1;
} }
public static T Deserialize<T>(string value) public static T Deserialize<T>(string value)
@ -168,13 +177,6 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1; return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1;
} }
public static T Deserialize<T>(object value)
{
var json = DefaultSerializer.Serialize(new ObjectHolder<object> { Value1 = value, Value2 = value });
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1;
}
public static string CleanJson(this string json) public static string CleanJson(this string json)
{ {
using var document = JsonDocument.Parse(json); using var document = JsonDocument.Parse(json);

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetQueryTests.cs

@ -9,11 +9,10 @@ using FakeItEasy;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;
using MongoDB.Driver; using MongoDB.Driver;
using NodaTime.Text; using NodaTime.Text;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.MongoDb.Assets; using Squidex.Domain.Apps.Entities.MongoDb.Assets;
using Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors; using Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
@ -29,12 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
static AssetQueryTests() static AssetQueryTests()
{ {
BsonDomainIdSerializer.Register(); TestUtils.SetupBson();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
BsonInstantSerializer.Register();
} }
[Fact] [Fact]

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryFixture.cs

@ -8,7 +8,6 @@
using System.Globalization; using System.Globalization;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Assets; using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;

32
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentQueryTests.cs

@ -11,14 +11,13 @@ using MongoDB.Driver;
using NodaTime.Text; using NodaTime.Text;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.MongoDb.Contents; using Squidex.Domain.Apps.Entities.MongoDb.Contents;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations; using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Xunit; using Xunit;
@ -35,12 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
static ContentQueryTests() static ContentQueryTests()
{ {
BsonDomainIdSerializer.Register(); TestUtils.SetupBson();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
BsonInstantSerializer.Register();
} }
public ContentQueryTests() public ContentQueryTests()
@ -63,8 +57,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
new ReferencesFieldProperties()) new ReferencesFieldProperties())
.AddString(8, "dashed-field", Partitioning.Invariant, .AddString(8, "dashed-field", Partitioning.Invariant,
new StringFieldProperties()) new StringFieldProperties())
.AddArray(9, "hobbies", Partitioning.Invariant, a => a .AddJson(9, "json", Partitioning.Invariant,
.AddString(91, "name")) new JsonFieldProperties())
.AddArray(10, "hobbies", Partitioning.Invariant, a => a
.AddString(101, "name"))
.Update(new SchemaProperties()); .Update(new SchemaProperties());
var schema = A.Dummy<ISchemaEntity>(); var schema = A.Dummy<ISchemaEntity>();
@ -180,6 +176,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
AssertQuery("{ 'do.dashed-field.iv' : 'Value' }", filter); AssertQuery("{ 'do.dashed-field.iv' : 'Value' }", filter);
} }
[Fact]
public void Should_make_query_with_json_dot_field()
{
var filter = ClrFilter.Eq("data/json/iv/with\\.dot", "Value");
AssertQuery("{ 'do.json.iv.with_§§_dot' : 'Value' }", filter);
}
[Fact]
public void Should_make_query_with_json_slash_field()
{
var filter = ClrFilter.Eq("data/json/iv/with\\/slash", "Value");
AssertQuery("{ 'do.json.iv.with/slash' : 'Value' }", filter);
}
[Fact] [Fact]
public void Should_make_query_with_references_equals() public void Should_make_query_with_references_equals()
{ {

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs

@ -11,7 +11,6 @@ using LoremNET;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/StatusSerializerTests.cs

@ -8,7 +8,7 @@
using MongoDB.Bson.IO; using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.MongoDb; using Squidex.Domain.Apps.Core.TestHelpers;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.MongoDb namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public StatusSerializerTests() public StatusSerializerTests()
{ {
BsonStringSerializer<Status>.Register(); TestUtils.SetupBson();
} }
[Fact] [Fact]

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/AtlasTextIndexFixture.cs

@ -11,7 +11,6 @@ using MongoDB.Driver;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.MongoDb.Text; using Squidex.Domain.Apps.Entities.MongoDb.Text;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.Text namespace Squidex.Domain.Apps.Entities.Contents.Text
@ -22,9 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
public AtlasTextIndexFixture() public AtlasTextIndexFixture()
{ {
BsonJsonConvention.Register(TestUtils.DefaultOptions()); TestUtils.SetupBson();
BsonDomainIdSerializer.Register();
var mongoClient = new MongoClient(TestConfig.Configuration["atlas:configuration"]); var mongoClient = new MongoClient(TestConfig.Configuration["atlas:configuration"]);
var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["atlas:database"]); var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["atlas:database"]);

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/MongoTextIndexFixture.cs

@ -9,7 +9,6 @@ using MongoDB.Driver;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.MongoDb.Text; using Squidex.Domain.Apps.Entities.MongoDb.Text;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.Text namespace Squidex.Domain.Apps.Entities.Contents.Text
@ -20,9 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
public MongoTextIndexFixture() public MongoTextIndexFixture()
{ {
BsonJsonConvention.Register(TestUtils.DefaultOptions()); TestUtils.SetupBson();
BsonDomainIdSerializer.Register();
var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]); var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]);
var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]); var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]);

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/MongoDb/SchemasHashFixture.cs

@ -6,9 +6,9 @@
// ========================================================================== // ==========================================================================
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.MongoDb.Schemas; using Squidex.Domain.Apps.Entities.MongoDb.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.Schemas.MongoDb namespace Squidex.Domain.Apps.Entities.Schemas.MongoDb
{ {
@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.MongoDb
public SchemasHashFixture() public SchemasHashFixture()
{ {
BsonInstantSerializer.Register(); TestUtils.SetupBson();
var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]); var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]);
var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]); var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]);

1
backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventFormatterTests.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using NodaTime; using NodaTime;
using Squidex.Infrastructure.Json.System;
using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.TestHelpers; using Squidex.Infrastructure.TestHelpers;

24
backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.TestHelpers; using Squidex.Infrastructure.TestHelpers;
using Xunit; using Xunit;
@ -39,9 +40,11 @@ namespace Squidex.Infrastructure.Json.Objects
[Fact] [Fact]
public void Should_deserialize_integer() public void Should_deserialize_integer()
{ {
var serialized = TestUtils.Deserialize<JsonValue>(123); var value = 123;
Assert.Equal(JsonValue.Create(123), serialized); var serialized = value.SerializeAndDeserialize<JsonValue, int>();
Assert.Equal(JsonValue.Create(value), serialized);
} }
[Theory] [Theory]
@ -152,5 +155,22 @@ namespace Squidex.Infrastructure.Json.Objects
Assert.Equal(value, serialized); Assert.Equal(value, serialized);
} }
[Fact]
public void Should_deserialize_from_escaped_dot()
{
var value = new Dictionary<string, int>
{
["key.with.dot".JsonToBsonName()] = 10
};
var expected =
new JsonObject()
.Add("key.with.dot", 10);
var serialized = TestUtils.SerializeAndDeserializeBson<JsonObject, Dictionary<string, int>>(value);
Assert.Equal(expected, serialized);
}
} }
} }

1
backend/tests/Squidex.Infrastructure.Tests/Json/System/JsonInheritanceConverterBaseTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.TestHelpers; using Squidex.Infrastructure.TestHelpers;
using Xunit; using Xunit;

3
backend/tests/Squidex.Infrastructure.Tests/MongoDb/DomainIdSerializerTests.cs

@ -9,6 +9,7 @@ using MongoDB.Bson;
using MongoDB.Bson.IO; using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Squidex.Infrastructure.TestHelpers;
using Xunit; using Xunit;
namespace Squidex.Infrastructure.MongoDb namespace Squidex.Infrastructure.MongoDb
@ -28,7 +29,7 @@ namespace Squidex.Infrastructure.MongoDb
public DomainIdSerializerTests() public DomainIdSerializerTests()
{ {
BsonDomainIdSerializer.Register(); TestUtils.SetupBson();
} }
[Fact] [Fact]

2
backend/tests/Squidex.Infrastructure.Tests/MongoDb/InstantSerializerTests.cs

@ -15,7 +15,7 @@ namespace Squidex.Infrastructure.MongoDb
{ {
public InstantSerializerTests() public InstantSerializerTests()
{ {
BsonInstantSerializer.Register(); TestUtils.SetupBson();
} }
[Fact] [Fact]

7
backend/tests/Squidex.Infrastructure.Tests/MongoDb/MongoQueryTests.cs

@ -12,6 +12,7 @@ using NodaTime;
using NodaTime.Text; using NodaTime.Text;
using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.TestHelpers;
using Xunit; using Xunit;
using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter; using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter;
using SortBuilder = Squidex.Infrastructure.Queries.SortBuilder; using SortBuilder = Squidex.Infrastructure.Queries.SortBuilder;
@ -35,11 +36,7 @@ namespace Squidex.Infrastructure.MongoDb
static MongoQueryTests() static MongoQueryTests()
{ {
BsonDomainIdSerializer.Register(); TestUtils.SetupBson();
BsonStringSerializer<RefToken>.Register();
BsonInstantSerializer.Register();
} }
[Fact] [Fact]

4
backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs

@ -126,7 +126,7 @@ namespace Squidex.Infrastructure
{ {
var value = new { id = 42L, name = "my-name" }; var value = new { id = 42L, name = "my-name" };
var serialized = value.SerializeAndDeserialize<NamedId<long>>(); var serialized = value.SerializeAndDeserialize<NamedId<long>, object>();
Assert.Equal(NamedId.Of(42L, "my-name"), serialized); Assert.Equal(NamedId.Of(42L, "my-name"), serialized);
} }
@ -144,7 +144,7 @@ namespace Squidex.Infrastructure
Value = NamedId.Of(42L, "my-name") Value = NamedId.Of(42L, "my-name")
}; };
var serialized = value.SerializeAndDeserialize<Wrapper>(); var serialized = value.SerializeAndDeserialize<Wrapper, object>();
Assert.Equal(expected, serialized); Assert.Equal(expected, serialized);
} }

102
backend/tests/Squidex.Infrastructure.Tests/Queries/PropertyPathTests.cs

@ -0,0 +1,102 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Xunit;
namespace Squidex.Infrastructure.Queries
{
public class PropertyPathTests
{
[Fact]
public void Should_create()
{
var path = new PropertyPath(new[] { "path", "to", "property" });
Assert.Equal(new[] { "path", "to", "property" }, path.ToArray());
}
[Fact]
public void Should_convert_to_string()
{
var path = new PropertyPath(new[] { "path", "to", "property" });
Assert.Equal("path.to.property", path.ToString());
}
[Fact]
public void Should_throw_exception_for_empty_path()
{
Assert.Throws<ArgumentException>(() => new PropertyPath(Array.Empty<string>()));
}
[Fact]
public void Should_throw_exception_for_empty_path_from_string()
{
Assert.Throws<ArgumentException>(() => { PropertyPath p = string.Empty; });
}
[Fact]
public void Should_throw_exception_for_empty_path_from_null_string()
{
Assert.Throws<ArgumentException>(() => { PropertyPath p = (string)null!; });
}
[Fact]
public void Should_throw_exception_for_empty_path_from_list()
{
Assert.Throws<ArgumentException>(() => { PropertyPath p = new List<string>(); });
}
[Fact]
public void Should_create_from_dot_string()
{
PropertyPath path = "path.to.property";
Assert.Equal(new[] { "path", "to", "property" }, path.ToArray());
}
[Fact]
public void Should_create_from_broken_dot_string()
{
PropertyPath path = ".path...to...property.";
Assert.Equal(new[] { "path", "to", "property" }, path.ToArray());
}
[Fact]
public void Should_create_from_slash_string()
{
PropertyPath path = "path/to/property";
Assert.Equal(new[] { "path", "to", "property" }, path.ToArray());
}
[Fact]
public void Should_create_from_broken_slash_string()
{
PropertyPath path = "/path///to///property/";
Assert.Equal(new[] { "path", "to", "property" }, path.ToArray());
}
[Fact]
public void Should_create_from_dot_string_and_escape()
{
PropertyPath path = "path.to.complex\\.property";
Assert.Equal(new[] { "path", "to", "complex.property" }, path.ToArray());
}
[Fact]
public void Should_create_from_slash_string_and_escape()
{
PropertyPath path = "path.to.complex\\/property";
Assert.Equal(new[] { "path", "to", "complex/property" }, path.ToArray());
}
}
}

30
backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs

@ -46,9 +46,11 @@ namespace Squidex.Infrastructure.TestHelpers
public static void SetupBson() public static void SetupBson()
{ {
BsonDomainIdSerializer.Register(); BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonInstantSerializer.Register(); BsonInstantSerializer.Register();
BsonJsonConvention.Register(DefaultOptions()); BsonJsonConvention.Register(DefaultOptions());
BsonJsonValueSerializer.Register(); BsonJsonValueSerializer.Register();
BsonStringSerializer<RefToken>.Register();
} }
public static IJsonSerializer CreateSerializer(Action<JsonSerializerOptions>? configure = null) public static IJsonSerializer CreateSerializer(Action<JsonSerializerOptions>? configure = null)
@ -99,34 +101,37 @@ namespace Squidex.Infrastructure.TestHelpers
} }
public static T SerializeAndDeserializeBson<T>(this T value) public static T SerializeAndDeserializeBson<T>(this T value)
{
return SerializeAndDeserializeBson<T, T>(value);
}
public static TOut SerializeAndDeserializeBson<TOut, TIn>(this TIn value)
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using (var writer = new BsonBinaryWriter(stream)) using (var writer = new BsonBinaryWriter(stream))
{ {
BsonSerializer.Serialize(writer, new ObjectHolder<T> { Value1 = value, Value2 = value }); BsonSerializer.Serialize(writer, new ObjectHolder<TIn> { Value1 = value, Value2 = value });
} }
stream.Position = 0; stream.Position = 0;
using (var reader = new BsonBinaryReader(stream)) using (var reader = new BsonBinaryReader(stream))
{ {
return BsonSerializer.Deserialize<ObjectHolder<T>>(reader).Value1; return BsonSerializer.Deserialize<ObjectHolder<TOut>>(reader).Value1;
} }
} }
public static T SerializeAndDeserialize<T>(this object value) public static T SerializeAndDeserialize<T>(this T value)
{ {
var json = DefaultSerializer.Serialize(value); return SerializeAndDeserialize<T, T>(value);
return DefaultSerializer.Deserialize<T>(json);
} }
public static T SerializeAndDeserialize<T>(this T value) public static TOut SerializeAndDeserialize<TOut, TIn>(this TIn value)
{ {
var json = DefaultSerializer.Serialize(new ObjectHolder<T> { Value1 = value, Value2 = value }); var json = DefaultSerializer.Serialize(new ObjectHolder<TIn> { Value1 = value, Value2 = value });
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1; return DefaultSerializer.Deserialize<ObjectHolder<TOut>>(json).Value1;
} }
public static T Deserialize<T>(string value) public static T Deserialize<T>(string value)
@ -136,13 +141,6 @@ namespace Squidex.Infrastructure.TestHelpers
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1; return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1;
} }
public static T Deserialize<T>(object value)
{
var json = DefaultSerializer.Serialize(new ObjectHolder<object> { Value1 = value, Value2 = value });
return DefaultSerializer.Deserialize<ObjectHolder<T>>(json).Value1;
}
public static string CleanJson(this string json) public static string CleanJson(this string json)
{ {
using var document = JsonDocument.Parse(json); using var document = JsonDocument.Parse(json);

5
backend/tools/TestSuite/TestSuite.ApiTests/BackupTests.cs

@ -134,7 +134,10 @@ namespace TestSuite.ApiTests
var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(appName, schemaName); var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(appName, schemaName);
await contents.CreateAsync(new TestEntityData { Number = 1 }); await contents.CreateAsync(new TestEntityData
{
Number = 1
});
// Upload a test asset // Upload a test asset

23
backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs

@ -35,11 +35,12 @@ namespace TestSuite.ApiTests
// STEP 2: Create a content for this schema. // STEP 2: Create a content for this schema.
var data = new TestEntityData { Number = 12, String = "hello" }; var content_1 = await contents.CreateAsync(new TestEntityData
{
var content_1 = await contents.CreateAsync(data); String = "hello"
});
Assert.Equal(data.String, content_1.Data.String); Assert.Equal("hello", content_1.Data.String);
// STEP 3: Delete a field from schema. // STEP 3: Delete a field from schema.
@ -66,15 +67,17 @@ namespace TestSuite.ApiTests
// STEP 2: Create a referenced content. // STEP 2: Create a referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await contents.CreateAsync(dataA); References = null
});
// STEP 3: Create a content with a reference. // STEP 3: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; var contentB_1 = await contents.CreateAsync(new TestEntityWithReferencesData
{
var contentB_1 = await contents.CreateAsync(dataB); References = new[] { contentA_1.Id }
});
// STEP 3: Delete a reference // STEP 3: Delete a reference

49
backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs

@ -319,6 +319,55 @@ namespace TestSuite.ApiTests
AssertItems(items, 1, new[] { 4 }); AssertItems(items, 1, new[] { 4 });
} }
[Fact]
public async Task Should_query_json_with_dot()
{
TestEntity content = null;
try
{
// STEP 1: Create a content item with a text that caused a bug before.
content = await _.Contents.CreateAsync(new TestEntityData
{
Json = new JObject
{
["search.field.with.dot"] = 42
}
}, ContentCreateOptions.AsPublish);
// STEP 2: Get the item and ensure that the text is the same.
var q = new ContentQuery
{
JsonQuery = new
{
filter = new
{
and = new[]
{
new
{
path = "data.json.iv.search\\.field\\.with\\.dot",
op = "eq",
value = 42
}
}
}
}
};
var queried = await _.Contents.GetAsync(q);
Assert.Equal(42, (int)queried.Items[0].Data.Json["search.field.with.dot"]);
}
finally
{
if (content != null)
{
await _.Contents.DeleteAsync(content.Id);
}
}
}
[Fact] [Fact]
public async Task Should_return_items_by_near_geoson_location_with_json() public async Task Should_return_items_by_near_geoson_location_with_json()
{ {

70
backend/tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs

@ -26,15 +26,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_deliver_unpublished_references() public async Task Should_not_deliver_unpublished_references()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await _.Contents.CreateAsync(dataA); References = null
});
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; var contentB_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentB_1 = await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish); References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Query new item // STEP 3: Query new item
@ -60,15 +62,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_delete_when_referenced() public async Task Should_not_delete_when_referenced()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish); References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish); References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
@ -88,15 +92,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_unpublish_when_referenced() public async Task Should_not_unpublish_when_referenced()
{ {
// STEP 1: Create a published referenced content. // STEP 1: Create a published referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish); References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish); References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Try to ThrowsAnyAsync with referrer check. // STEP 3: Try to ThrowsAnyAsync with referrer check.
@ -124,15 +130,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_delete_with_bulk_when_referenced() public async Task Should_not_delete_with_bulk_when_referenced()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish); References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish); References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
@ -175,15 +183,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_unpublish_with_bulk_when_referenced() public async Task Should_not_unpublish_with_bulk_when_referenced()
{ {
// STEP 1: Create a published referenced content. // STEP 1: Create a published referenced content.
var dataA = new TestEntityWithReferencesData(); var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish); References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a published content with a reference. // STEP 2: Create a published content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } }; await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish); References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.

15
backend/tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs

@ -43,7 +43,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create content // STEP 2: Create content
var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName); var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData { Number = 13 }); var content = await contents.CreateAsync(new TestEntityData
{
Number = 13
});
Assert.Equal(26, content.Data.Number); Assert.Equal(26, content.Data.Number);
} }
@ -65,7 +68,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create content // STEP 2: Create content
var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName); var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData { Number = 13 }, ContentCreateOptions.AsPublish); var content = await contents.CreateAsync(new TestEntityData
{
Number = 13
}, ContentCreateOptions.AsPublish);
Assert.Equal(26, content.Data.Number); Assert.Equal(26, content.Data.Number);
} }
@ -89,7 +95,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create content // STEP 2: Create content
var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName); var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData { Number = 99 }, ContentCreateOptions.AsPublish); var content = await contents.CreateAsync(new TestEntityData
{
Number = 99
}, ContentCreateOptions.AsPublish);
Assert.Equal(19, content.Data.Number); Assert.Equal(19, content.Data.Number);
} }

24
backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.Should_create_json_with_dot.verified.txt

@ -0,0 +1,24 @@
{
Data: {
Json: {
iv: null
}
},
Status: Published,
Id: Guid_1,
Version: 1,
_links: {
delete: {
Method: DELETE
},
draft/create: {
Method: POST
},
previous: {
Method: GET
},
self: {
Method: GET
}
}
}

262
backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

@ -33,7 +33,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
});
// STEP 2: Publish the item. // STEP 2: Publish the item.
@ -62,7 +65,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create the item published. // STEP 1: Create the item published.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, ContentCreateOptions.AsPublish);
// STEP 2: Archive the item. // STEP 2: Archive the item.
@ -94,7 +100,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
});
// STEP 2: Change the status to publiushed and then to draft. // STEP 2: Change the status to publiushed and then to draft.
@ -132,15 +141,18 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a content item with a text that caused a bug before. // STEP 1: Create a content item with a text that caused a bug before.
content = await _.Contents.CreateAsync(new TestEntityData { String = text }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = text
}, ContentCreateOptions.AsPublish);
// STEP 2: Get the item and ensure that the text is the same. // STEP 2: Get the item and ensure that the text is the same.
var updated = await _.Contents.GetAsync(content.Id); var queried = await _.Contents.GetAsync(content.Id);
Assert.Equal(text, updated.Data.String); Assert.Equal(text, queried.Data.String);
await Verify(updated); await Verify(queried);
} }
finally finally
{ {
@ -168,11 +180,43 @@ namespace TestSuite.ApiTests
// STEP 2: Get the item and ensure that the text is the same. // STEP 2: Get the item and ensure that the text is the same.
var updated = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback()); var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback());
Assert.Null(updated.Data.Localized["en"]); Assert.Null(queried.Data.Localized["en"]);
await Verify(updated); await Verify(queried);
}
finally
{
if (content != null)
{
await _.Contents.DeleteAsync(content.Id);
}
}
}
[Fact]
public async Task Should_create_json_with_dot()
{
TestEntity content = null;
try
{
// STEP 1: Create a content item with a text that caused a bug before.
content = await _.Contents.CreateAsync(new TestEntityData
{
Json = new JObject
{
["field.with.dot"] = 42
}
}, ContentCreateOptions.AsPublish);
// STEP 2: Get the item and ensure that the text is the same.
var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback());
Assert.Equal(42, (int)queried.Data.Json["field.with.dot"]);
await Verify(queried);
} }
finally finally
{ {
@ -217,7 +261,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
});
// STEP 2. Get a 404 for the item because it is not published. // STEP 2. Get a 404 for the item because it is not published.
@ -244,7 +291,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create the item published. // STEP 1: Create the item published.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, ContentCreateOptions.AsPublish);
// STEP 2: Get the item. // STEP 2: Get the item.
@ -270,7 +320,10 @@ namespace TestSuite.ApiTests
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var options = new ContentCreateOptions { Id = id, Publish = true };
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, options); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, options);
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
} }
@ -294,7 +347,10 @@ namespace TestSuite.ApiTests
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var options = new ContentCreateOptions { Id = id, Publish = true };
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, options); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, options);
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
@ -302,7 +358,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create a new item with a custom id. // STEP 2: Create a new item with a custom id.
var ex = await Assert.ThrowsAnyAsync<SquidexException>(() => var ex = await Assert.ThrowsAnyAsync<SquidexException>(() =>
{ {
return _.Contents.CreateAsync(new TestEntityData { Number = 1 }, options); return _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, options);
}); });
Assert.Equal(409, ex.StatusCode); Assert.Equal(409, ex.StatusCode);
@ -325,19 +384,28 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Upsert a new item with a custom id. // STEP 1: Upsert a new item with a custom id.
content = await _.Contents.UpsertAsync(id, new TestEntityData { Number = 1 }, ContentUpsertOptions.AsPublish); content = await _.Contents.UpsertAsync(id, new TestEntityData
{
Number = 1
}, ContentUpsertOptions.AsPublish);
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
// STEP 2: Make an update with the upsert endpoint. // STEP 2: Make an update with the upsert endpoint.
content = await _.Contents.UpsertAsync(id, new TestEntityData { Number = 2 }); content = await _.Contents.UpsertAsync(id, new TestEntityData
{
Number = 2
});
Assert.Equal(2, content.Data.Number); Assert.Equal(2, content.Data.Number);
// STEP 3: Make an update with the update endpoint. // STEP 3: Make an update with the update endpoint.
content = await _.Contents.UpdateAsync(id, new TestEntityData { Number = 3 }); content = await _.Contents.UpdateAsync(id, new TestEntityData
{
Number = 3
});
Assert.Equal(3, content.Data.Number); Assert.Equal(3, content.Data.Number);
} }
@ -357,11 +425,17 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
// STEP 2: Update the item and ensure that the data has changed. // STEP 2: Update the item and ensure that the data has changed.
await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = 2 }); await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = 2
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -383,7 +457,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
var numErrors = 0; var numErrors = 0;
@ -394,7 +471,10 @@ namespace TestSuite.ApiTests
{ {
try try
{ {
await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = i }); await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = i
});
Interlocked.Increment(ref numSuccess); Interlocked.Increment(ref numSuccess);
} }
@ -411,7 +491,10 @@ namespace TestSuite.ApiTests
// STEP 3: Make an normal update to ensure nothing is corrupt. // STEP 3: Make an normal update to ensure nothing is corrupt.
await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = 2 }); await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = 2
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -433,7 +516,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
var numErrors = 0; var numErrors = 0;
@ -444,7 +530,10 @@ namespace TestSuite.ApiTests
{ {
try try
{ {
await _.Contents.UpsertAsync(content.Id, new TestEntityData { Number = i }); await _.Contents.UpsertAsync(content.Id, new TestEntityData
{
Number = i
});
Interlocked.Increment(ref numSuccess); Interlocked.Increment(ref numSuccess);
} }
@ -461,7 +550,10 @@ namespace TestSuite.ApiTests
// STEP 3: Make an normal update to ensure nothing is corrupt. // STEP 3: Make an normal update to ensure nothing is corrupt.
await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = 2 }); await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = 2
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -483,11 +575,17 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "initial" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "initial"
}, ContentCreateOptions.AsPublish);
// STEP 2: Update the item and ensure that the data has changed. // STEP 2: Update the item and ensure that the data has changed.
await _.Contents.UpdateAsync(content.Id, new TestEntityData { String = null }); await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
String = null
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -509,15 +607,24 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "test"
}, ContentCreateOptions.AsPublish);
// STEP 2: Patch an item. // STEP 2: Patch an item.
await _.Contents.PatchAsync(content.Id, new TestEntityData { Number = 1 }); await _.Contents.PatchAsync(content.Id, new TestEntityData
{
Number = 1
});
// STEP 3: Update the item and ensure that the data has changed. // STEP 3: Update the item and ensure that the data has changed.
await _.Contents.PatchAsync(content.Id, new TestEntityData { Number = 2 }); await _.Contents.PatchAsync(content.Id, new TestEntityData
{
Number = 2
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -544,11 +651,17 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Id = "id1" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Id = "id1"
}, ContentCreateOptions.AsPublish);
// STEP 2: Update the item and ensure that the data has changed. // STEP 2: Update the item and ensure that the data has changed.
await _.Contents.PatchAsync(content.Id, new TestEntityData { Id = "id2" }); await _.Contents.PatchAsync(content.Id, new TestEntityData
{
Id = "id2"
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -572,11 +685,20 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "initial" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "initial"
}, ContentCreateOptions.AsPublish);
// STEP 2: Update the item and ensure that the data has changed. // STEP 2: Update the item and ensure that the data has changed.
await _.Contents.PatchAsync(content.Id, new { @string = new { iv = (object)null } }); await _.Contents.PatchAsync(content.Id, new
{
@string = new
{
iv = (object)null
}
});
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -600,15 +722,24 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "test"
}, ContentCreateOptions.AsPublish);
// STEP 2: Patch an item. // STEP 2: Patch an item.
await _.Contents.UpsertAsync(content.Id, new TestEntityData { Number = 1 }, ContentUpsertOptions.AsPatch); await _.Contents.UpsertAsync(content.Id, new TestEntityData
{
Number = 1
}, ContentUpsertOptions.AsPatch);
// STEP 3: Update the item and ensure that the data has changed. // STEP 3: Update the item and ensure that the data has changed.
await _.Contents.UpsertAsync(content.Id, new TestEntityData { Number = 2 }, ContentUpsertOptions.AsPatch); await _.Contents.UpsertAsync(content.Id, new TestEntityData
{
Number = 2
}, ContentUpsertOptions.AsPatch);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -635,7 +766,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "test"
}, ContentCreateOptions.AsPublish);
// STEP 2: Patch an item. // STEP 2: Patch an item.
@ -719,7 +853,10 @@ namespace TestSuite.ApiTests
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
String = "test"
}, ContentCreateOptions.AsPublish);
// STEP 2: Patch an item. // STEP 2: Patch an item.
@ -785,7 +922,10 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, ContentCreateOptions.AsPublish);
// STEP 2: Create draft. // STEP 2: Create draft.
@ -793,7 +933,10 @@ namespace TestSuite.ApiTests
// STEP 3: Update the item and ensure that the data has not changed. // STEP 3: Update the item and ensure that the data has not changed.
await _.Contents.PatchAsync(content.Id, new TestEntityData { Number = 2 }); await _.Contents.PatchAsync(content.Id, new TestEntityData
{
Number = 2
});
var updated_1 = await _.Contents.GetAsync(content.Id); var updated_1 = await _.Contents.GetAsync(content.Id);
@ -831,7 +974,10 @@ namespace TestSuite.ApiTests
public async Task Should_delete_content(bool permanent) public async Task Should_delete_content(bool permanent)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content_1 = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); var content_1 = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
// STEP 2: Delete the item. // STEP 2: Delete the item.
@ -858,7 +1004,10 @@ namespace TestSuite.ApiTests
public async Task Should_recreate_deleted_content(bool permanent) public async Task Should_recreate_deleted_content(bool permanent)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content_1 = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); var content_1 = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
// STEP 2: Delete the item. // STEP 2: Delete the item.
@ -870,7 +1019,10 @@ namespace TestSuite.ApiTests
// STEP 3: Recreate the item with the same id. // STEP 3: Recreate the item with the same id.
var deleteOptions = new ContentCreateOptions { Id = content_1.Id, Publish = true }; var deleteOptions = new ContentCreateOptions { Id = content_1.Id, Publish = true };
var content_2 = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, deleteOptions); var content_2 = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, deleteOptions);
Assert.Equal(Status.Published, content_2.Status); Assert.Equal(Status.Published, content_2.Status);
@ -889,7 +1041,10 @@ namespace TestSuite.ApiTests
public async Task Should_recreate_deleted_content_with_upsert(bool permanent) public async Task Should_recreate_deleted_content_with_upsert(bool permanent)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content_1 = await _.Contents.CreateAsync(new TestEntityData { Number = 2 }, ContentCreateOptions.AsPublish); var content_1 = await _.Contents.CreateAsync(new TestEntityData
{
Number = 2
}, ContentCreateOptions.AsPublish);
// STEP 2: Delete the item. // STEP 2: Delete the item.
@ -899,7 +1054,10 @@ namespace TestSuite.ApiTests
// STEP 3: Recreate the item with the same id. // STEP 3: Recreate the item with the same id.
var content_2 = await _.Contents.UpsertAsync(content_1.Id, new TestEntityData { Number = 2 }, ContentUpsertOptions.AsPublish); var content_2 = await _.Contents.UpsertAsync(content_1.Id, new TestEntityData
{
Number = 2
}, ContentUpsertOptions.AsPublish);
Assert.Equal(Status.Published, content_2.Status); Assert.Equal(Status.Published, content_2.Status);
@ -965,11 +1123,17 @@ namespace TestSuite.ApiTests
try try
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData { Number = 1 }, ContentCreateOptions.AsPublish); content = await _.Contents.CreateAsync(new TestEntityData
{
Number = 1
}, ContentCreateOptions.AsPublish);
// STEP 2: Update content. // STEP 2: Update content.
content = await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = 2 }); content = await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = 2
});
// STEP 3: Get current version. // STEP 3: Get current version.

5
backend/tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

@ -176,7 +176,10 @@ namespace TestSuite.ApiTests
// Create a test content. // Create a test content.
var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(appName, schemaName); var contents = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(appName, schemaName);
await contents.CreateAsync(new TestEntityData { String = contentString }); await contents.CreateAsync(new TestEntityData
{
String = contentString
});
} }
private async Task CreateAppAsync() private async Task CreateAppAsync()

5
backend/tools/TestSuite/TestSuite.LoadTests/WritingBenchmarks.cs

@ -62,7 +62,10 @@ namespace TestSuite.LoadTests
await Run.Parallel(numUsers, numIterationsPerUser, async () => await Run.Parallel(numUsers, numIterationsPerUser, async () =>
{ {
await _.Contents.CreateAsync(new TestEntityData { Number = random.Next() }, ContentCreateOptions.AsPublish); await _.Contents.CreateAsync(new TestEntityData
{
Number = random.Next()
}, ContentCreateOptions.AsPublish);
}); });
} }
} }

Loading…
Cancel
Save