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.Infrastructure;
using Squidex.Infrastructure.Queries;
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.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps;
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.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps;
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.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver;
using NodaTime;
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.Hosting;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries;
@ -32,6 +33,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
static MongoContentRepository()
{
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonEscapedDictionarySerializer<JsonValue, ContentFieldData>.Register();
BsonEscapedDictionarySerializer<ContentFieldData, ContentData>.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++)
{
result[i] = result[i].UnescapeEdmField().JsonEscape();
result[i] = result[i].UnescapeEdmField().JsonToBsonName().JsonEscape();
}
return result;

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

@ -7,7 +7,6 @@
using System.Runtime.CompilerServices;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents;
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.
// ==========================================================================
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
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.
// ==========================================================================
using System.Runtime.Serialization;
using Squidex.Infrastructure;
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 TypeBson = "§type";
private const string TypeJson = "$type";
private const string DotSource = ".";
private const string DotReplacement = "_§§_";
private const string DotBson = "_§§_";
private const string DotJson = ".";
public static string UnescapeBson(this string value)
public static string BsonToJsonName(this string value)
{
if (value == Empty)
{
@ -27,12 +27,12 @@ namespace Squidex.Infrastructure.MongoDb
return TypeJson;
}
var result = value.ReplaceFirst('§', '$').Replace(DotReplacement, DotSource, StringComparison.Ordinal);
var result = value.ReplaceFirst('§', '$').Replace(DotBson, DotJson, StringComparison.Ordinal);
return result;
}
public static string EscapeJson(this string value)
public static string JsonToBsonName(this string value)
{
if (value.Length == 0)
{
@ -44,7 +44,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeBson;
}
var result = value.ReplaceFirst('$', '§').Replace(DotSource, DotReplacement, StringComparison.Ordinal);
var result = value.ReplaceFirst('$', '§').Replace(DotJson, DotBson, StringComparison.Ordinal);
return result;
}

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

@ -6,7 +6,6 @@
// ==========================================================================
using System.Reflection;
using System.Reflection.Metadata;
using System.Text.Json;
using MongoDB.Bson;
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
{
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; }
@ -134,7 +134,7 @@ namespace Squidex.Infrastructure.MongoDb
Read();
break;
case BsonReaderState.Name:
writer.WritePropertyName(reader.ReadName().UnescapeBson());
writer.WritePropertyName(reader.ReadName().BsonToJsonName());
Read();
break;
case BsonReaderState.Value:
@ -247,7 +247,7 @@ namespace Squidex.Infrastructure.MongoDb
foreach (var property in element.EnumerateObject())
{
writer.WriteName(property.Name.EscapeJson());
writer.WriteName(property.Name.JsonToBsonName());
WriteElement(writer, property.Value);
}

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

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

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

@ -6,7 +6,6 @@
// ==========================================================================
using System.Runtime.CompilerServices;
using MongoDB.Bson;
using MongoDB.Driver;
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.
// ==========================================================================
using System.Text.Json;
using Squidex.Infrastructure.Reflection;
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.
// ==========================================================================
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.Json;
using System.Text.Json.Serialization;
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)
{
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)
@ -42,6 +88,13 @@ namespace Squidex.Infrastructure.Queries
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)
{
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.Net.Http.Headers;
using Squidex.Assets;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;

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

@ -8,7 +8,6 @@
using GraphQL;
using GraphQL.DataLoader;
using GraphQL.DI;
using GraphQL.Execution;
using GraphQL.MicrosoftDI;
using GraphQL.Server.Transports.AspNetCore;
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",
"Permission2"));
var roles = source.SerializeAndDeserialize<Roles>();
var roles = source.SerializeAndDeserialize<Roles, Dictionary<string, string[]>>();
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 serialized = jsonStep.SerializeAndDeserialize<WorkflowStep>();
var serialized = jsonStep.SerializeAndDeserialize<WorkflowStep, object>();
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)
.Publish();
var schemaTarget = schemaSource.SerializeAndDeserialize<Schema>();
var schemaTarget = schemaSource.SerializeAndDeserialize<Schema, object>();
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()
{
BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<ContentFieldData, ContentData>.Register();
BsonEscapedDictionarySerializer<JsonValue, ContentFieldData>.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonInstantSerializer.Register();
BsonJsonConvention.Register(DefaultOptions());
BsonJsonValueSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
}
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)
{
return SerializeAndDeserializeBson<T, T>(value);
}
public static TOut SerializeAndDeserializeBson<TOut, TIn>(this TIn value)
{
using var stream = new MemoryStream();
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;
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 DefaultSerializer.Deserialize<T>(json);
return SerializeAndDeserialize<T, T>(value);
}
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)
@ -168,13 +177,6 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
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)
{
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.Driver;
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.Visitors;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Validation;
@ -29,12 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
static AssetQueryTests()
{
BsonDomainIdSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
BsonInstantSerializer.Register();
TestUtils.SetupBson();
}
[Fact]

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

@ -8,7 +8,6 @@
using System.Globalization;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using NodaTime;
using Squidex.Domain.Apps.Core.Assets;
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 Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.MongoDb.Contents;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
using Xunit;
@ -35,12 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
static ContentQueryTests()
{
BsonDomainIdSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
BsonInstantSerializer.Register();
TestUtils.SetupBson();
}
public ContentQueryTests()
@ -63,8 +57,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
new ReferencesFieldProperties())
.AddString(8, "dashed-field", Partitioning.Invariant,
new StringFieldProperties())
.AddArray(9, "hobbies", Partitioning.Invariant, a => a
.AddString(91, "name"))
.AddJson(9, "json", Partitioning.Invariant,
new JsonFieldProperties())
.AddArray(10, "hobbies", Partitioning.Invariant, a => a
.AddString(101, "name"))
.Update(new SchemaProperties());
var schema = A.Dummy<ISchemaEntity>();
@ -180,6 +176,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
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]
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 MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using NodaTime;
using Squidex.Domain.Apps.Core;
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.Serialization;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.MongoDb;
using Squidex.Domain.Apps.Core.TestHelpers;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public StatusSerializerTests()
{
BsonStringSerializer<Status>.Register();
TestUtils.SetupBson();
}
[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.Entities.MongoDb.Text;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.Text
@ -22,9 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
public AtlasTextIndexFixture()
{
BsonJsonConvention.Register(TestUtils.DefaultOptions());
BsonDomainIdSerializer.Register();
TestUtils.SetupBson();
var mongoClient = new MongoClient(TestConfig.Configuration["atlas:configuration"]);
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.Entities.MongoDb.Text;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.Text
@ -20,9 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
public MongoTextIndexFixture()
{
BsonJsonConvention.Register(TestUtils.DefaultOptions());
BsonDomainIdSerializer.Register();
TestUtils.SetupBson();
var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]);
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 Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.MongoDb.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.Schemas.MongoDb
{
@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.MongoDb
public SchemasHashFixture()
{
BsonInstantSerializer.Register();
TestUtils.SetupBson();
var mongoClient = new MongoClient(TestConfig.Configuration["mongodb:configuration"]);
var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]);

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

@ -6,7 +6,6 @@
// ==========================================================================
using NodaTime;
using Squidex.Infrastructure.Json.System;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.Reflection;
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.
// ==========================================================================
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
@ -39,9 +40,11 @@ namespace Squidex.Infrastructure.Json.Objects
[Fact]
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]
@ -152,5 +155,22 @@ namespace Squidex.Infrastructure.Json.Objects
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.
// ==========================================================================
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.TestHelpers;
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.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
namespace Squidex.Infrastructure.MongoDb
@ -28,7 +29,7 @@ namespace Squidex.Infrastructure.MongoDb
public DomainIdSerializerTests()
{
BsonDomainIdSerializer.Register();
TestUtils.SetupBson();
}
[Fact]

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

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

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

@ -12,6 +12,7 @@ using NodaTime;
using NodaTime.Text;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter;
using SortBuilder = Squidex.Infrastructure.Queries.SortBuilder;
@ -35,11 +36,7 @@ namespace Squidex.Infrastructure.MongoDb
static MongoQueryTests()
{
BsonDomainIdSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonInstantSerializer.Register();
TestUtils.SetupBson();
}
[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 serialized = value.SerializeAndDeserialize<NamedId<long>>();
var serialized = value.SerializeAndDeserialize<NamedId<long>, object>();
Assert.Equal(NamedId.Of(42L, "my-name"), serialized);
}
@ -144,7 +144,7 @@ namespace Squidex.Infrastructure
Value = NamedId.Of(42L, "my-name")
};
var serialized = value.SerializeAndDeserialize<Wrapper>();
var serialized = value.SerializeAndDeserialize<Wrapper, object>();
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()
{
BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonInstantSerializer.Register();
BsonJsonConvention.Register(DefaultOptions());
BsonJsonValueSerializer.Register();
BsonStringSerializer<RefToken>.Register();
}
public static IJsonSerializer CreateSerializer(Action<JsonSerializerOptions>? configure = null)
@ -99,34 +101,37 @@ namespace Squidex.Infrastructure.TestHelpers
}
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 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;
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 DefaultSerializer.Deserialize<T>(json);
return SerializeAndDeserialize<T, T>(value);
}
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)
@ -136,13 +141,6 @@ namespace Squidex.Infrastructure.TestHelpers
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)
{
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);
await contents.CreateAsync(new TestEntityData { Number = 1 });
await contents.CreateAsync(new TestEntityData
{
Number = 1
});
// 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.
var data = new TestEntityData { Number = 12, String = "hello" };
var content_1 = await contents.CreateAsync(data);
var content_1 = await contents.CreateAsync(new TestEntityData
{
String = "hello"
});
Assert.Equal(data.String, content_1.Data.String);
Assert.Equal("hello", content_1.Data.String);
// STEP 3: Delete a field from schema.
@ -66,15 +67,17 @@ namespace TestSuite.ApiTests
// STEP 2: Create a referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await contents.CreateAsync(dataA);
var contentA_1 = await contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
});
// STEP 3: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
var contentB_1 = await contents.CreateAsync(dataB);
var contentB_1 = await contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
});
// 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 });
}
[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]
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()
{
// STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await _.Contents.CreateAsync(dataA);
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
});
// STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
var contentB_1 = await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish);
var contentB_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Query new item
@ -60,15 +62,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_delete_when_referenced()
{
// STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish);
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish);
await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check.
@ -88,15 +92,17 @@ namespace TestSuite.ApiTests
public async Task Should_not_unpublish_when_referenced()
{
// STEP 1: Create a published referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish);
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish);
await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// 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()
{
// STEP 1: Create a referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish);
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish);
await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// 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()
{
// STEP 1: Create a published referenced content.
var dataA = new TestEntityWithReferencesData();
var contentA_1 = await _.Contents.CreateAsync(dataA, ContentCreateOptions.AsPublish);
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = null
}, ContentCreateOptions.AsPublish);
// STEP 2: Create a published content with a reference.
var dataB = new TestEntityWithReferencesData { References = new[] { contentA_1.Id } };
await _.Contents.CreateAsync(dataB, ContentCreateOptions.AsPublish);
await _.Contents.CreateAsync(new TestEntityWithReferencesData
{
References = new[] { contentA_1.Id }
}, ContentCreateOptions.AsPublish);
// 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
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);
}
@ -65,7 +68,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create content
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);
}
@ -89,7 +95,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create content
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);
}

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
{
// 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.
@ -62,7 +65,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -94,7 +100,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -132,15 +141,18 @@ namespace TestSuite.ApiTests
try
{
// 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.
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
{
@ -168,11 +180,43 @@ namespace TestSuite.ApiTests
// 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
{
@ -217,7 +261,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -244,7 +291,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -270,7 +320,10 @@ namespace TestSuite.ApiTests
// STEP 1: Create a new item with a custom id.
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);
}
@ -294,7 +347,10 @@ namespace TestSuite.ApiTests
// STEP 1: Create a new item with a custom id.
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);
@ -302,7 +358,10 @@ namespace TestSuite.ApiTests
// STEP 2: Create a new item with a custom id.
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);
@ -325,19 +384,28 @@ namespace TestSuite.ApiTests
try
{
// 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);
// 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);
// 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);
}
@ -357,11 +425,17 @@ namespace TestSuite.ApiTests
try
{
// 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.
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);
@ -383,7 +457,10 @@ namespace TestSuite.ApiTests
try
{
// 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;
@ -394,7 +471,10 @@ namespace TestSuite.ApiTests
{
try
{
await _.Contents.UpdateAsync(content.Id, new TestEntityData { Number = i });
await _.Contents.UpdateAsync(content.Id, new TestEntityData
{
Number = i
});
Interlocked.Increment(ref numSuccess);
}
@ -411,7 +491,10 @@ namespace TestSuite.ApiTests
// 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);
@ -433,7 +516,10 @@ namespace TestSuite.ApiTests
try
{
// 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;
@ -444,7 +530,10 @@ namespace TestSuite.ApiTests
{
try
{
await _.Contents.UpsertAsync(content.Id, new TestEntityData { Number = i });
await _.Contents.UpsertAsync(content.Id, new TestEntityData
{
Number = i
});
Interlocked.Increment(ref numSuccess);
}
@ -461,7 +550,10 @@ namespace TestSuite.ApiTests
// 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);
@ -483,11 +575,17 @@ namespace TestSuite.ApiTests
try
{
// 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.
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);
@ -509,15 +607,24 @@ namespace TestSuite.ApiTests
try
{
// 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.
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.
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);
@ -544,11 +651,17 @@ namespace TestSuite.ApiTests
try
{
// 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.
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);
@ -572,11 +685,20 @@ namespace TestSuite.ApiTests
try
{
// 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.
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);
@ -600,15 +722,24 @@ namespace TestSuite.ApiTests
try
{
// 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.
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.
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);
@ -635,7 +766,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -719,7 +853,10 @@ namespace TestSuite.ApiTests
// 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.
@ -785,7 +922,10 @@ namespace TestSuite.ApiTests
try
{
// 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.
@ -793,7 +933,10 @@ namespace TestSuite.ApiTests
// 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);
@ -831,7 +974,10 @@ namespace TestSuite.ApiTests
public async Task Should_delete_content(bool permanent)
{
// 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.
@ -858,7 +1004,10 @@ namespace TestSuite.ApiTests
public async Task Should_recreate_deleted_content(bool permanent)
{
// 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.
@ -870,7 +1019,10 @@ namespace TestSuite.ApiTests
// STEP 3: Recreate the item with the same id.
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);
@ -889,7 +1041,10 @@ namespace TestSuite.ApiTests
public async Task Should_recreate_deleted_content_with_upsert(bool permanent)
{
// 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.
@ -899,7 +1054,10 @@ namespace TestSuite.ApiTests
// 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);
@ -965,11 +1123,17 @@ namespace TestSuite.ApiTests
try
{
// 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.
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.

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

@ -176,7 +176,10 @@ namespace TestSuite.ApiTests
// Create a test content.
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()

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

@ -62,7 +62,10 @@ namespace TestSuite.LoadTests
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