Browse Source

Random (#935)

* Random.

* Tests for random operator.
pull/936/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
c16e5311ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  2. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  3. 26
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs
  4. 8
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs
  5. 6
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs
  6. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs
  7. 7
      backend/src/Squidex.Domain.Apps.Entities/Contents/Text/Extensions.cs
  8. 36
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs
  9. 49
      backend/src/Squidex.Infrastructure/CollectionExtensions.cs
  10. 1
      backend/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs
  11. 17
      backend/src/Squidex.Infrastructure/Queries/OData/LimitExtensions.cs
  12. 29
      backend/src/Squidex.Infrastructure/Queries/Query.cs
  13. 21
      backend/src/Squidex.Infrastructure/StringExtensions.cs
  14. 6
      backend/src/Squidex.Web/ExposedValues.cs
  15. 7
      backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs
  16. 1
      backend/src/Squidex/Squidex.csproj
  17. 11
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryFixture.cs
  18. 35
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryIntegrationTests.cs
  19. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs
  20. 23
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryDedicatedIntegrationTests.cs
  21. 18
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs
  22. 219
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryIntegrationTests.cs
  23. 235
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryTestsBase.cs
  24. 46
      backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs
  25. 46
      backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs
  26. 4
      backend/tests/Squidex.Infrastructure.Tests/Tasks/PartitionedActionBlockTests.cs
  27. 171
      backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  28. 4
      backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

6
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs

@ -123,11 +123,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
void AddValue(object value)
{
if (sb.Length > 0)
{
sb.Append(separator);
}
sb.AppendIfNotEmpty(separator);
sb.Append(value);
}

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -94,10 +94,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
var filter = BuildFilter(appId, q.Ids.ToHashSet());
var assetEntities =
await Collection.Find(filter).SortByDescending(x => x.LastModified).ThenBy(x => x.Id)
await Collection.Find(filter)
.SortByDescending(x => x.LastModified).ThenBy(x => x.Id)
.QueryLimit(q.Query)
.QuerySkip(q.Query)
.ToListAsync(ct);
.ToListRandomAsync(Collection, q.Query.Random, ct);
long assetTotal = assetEntities.Count;
if (assetEntities.Count >= q.Query.Take || q.Query.Skip > 0)
@ -126,7 +127,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
.QueryLimit(query)
.QuerySkip(query)
.QuerySort(query)
.ToListAsync(ct);
.ToListRandomAsync(Collection, query.Random, ct);
long assetTotal = assetEntities.Count;
if (assetEntities.Count >= q.Query.Take || q.Query.Skip > 0)

26
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs

@ -9,6 +9,7 @@ using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
@ -55,8 +56,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
query.Sort[1].Order == SortOrder.Ascending;
}
public static async Task<List<MongoContentEntity>> QueryContentsAsync(this IMongoCollection<MongoContentEntity> collection,
FilterDefinition<MongoContentEntity> filter, ClrQuery query,
public static async Task<List<MongoContentEntity>> QueryContentsAsync(this IMongoCollection<MongoContentEntity> collection, FilterDefinition<MongoContentEntity> filter, ClrQuery query,
CancellationToken ct)
{
if (query.Skip > 0 && !query.IsSatisfiedByIndex())
@ -70,6 +70,26 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
projection = projection.Include(field);
}
if (query.Random > 0)
{
var ids =
await collection.Aggregate()
.Match(filter)
.Project<IdOnly>(projection)
.QuerySort(query)
.QuerySkip(query)
.QueryLimit(query)
.ToListAsync(ct);
var randomIds = ids.Select(x => x.Id).TakeRandom(query.Random);
var documents =
await collection.Find(Builders<MongoContentEntity>.Filter.In(x => x.Id, randomIds))
.ToListAsync(ct);
return documents.Shuffle().ToList();
}
var joined =
await collection.Aggregate()
.Match(filter)
@ -88,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
.QuerySort(query)
.QueryLimit(query)
.QuerySkip(query)
.ToListAsync(ct);
.ToListRandomAsync(collection, query.Random, ct);
return await result;
}

8
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs

@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
@ -44,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
var filter = CreateFilter(app.Id, schemas.Select(x => x.Id), q.Ids.ToHashSet());
var contentEntities = await FindContentsAsync(q.Query, filter);
var contentEntities = await FindContentsAsync(q.Query, filter, ct);
var contentTotal = (long)contentEntities.Count;
if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
@ -62,13 +63,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return ResultList.Create(contentTotal, contentEntities);
}
private async Task<List<MongoContentEntity>> FindContentsAsync(ClrQuery query, FilterDefinition<MongoContentEntity> filter)
private async Task<List<MongoContentEntity>> FindContentsAsync(ClrQuery query, FilterDefinition<MongoContentEntity> filter,
CancellationToken ct)
{
var result =
Collection.Find(filter)
.QueryLimit(query)
.QuerySkip(query)
.ToListAsync();
.ToListRandomAsync(Collection, query.Random, ct);
return await result;
}

6
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs

@ -99,11 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
if (!string.IsNullOrWhiteSpace(text))
{
if (sb.Length > 0)
{
sb.Append(", ");
}
sb.AppendIfNotEmpty(", ");
sb.Append(text);
}
}

6
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs

@ -125,11 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (!string.IsNullOrWhiteSpace(formatted))
{
if (sb.Length > 0)
{
sb.Append(", ");
}
sb.AppendIfNotEmpty(", ");
sb.Append(formatted);
}
}

7
backend/src/Squidex.Domain.Apps.Entities/Contents/Text/Extensions.cs

@ -8,6 +8,7 @@
using System.Text;
using NetTopologySuite.Geometries;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.ObjectPool;
@ -116,11 +117,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
languages[language] = sb;
}
if (sb.Length > 0)
{
sb.Append(' ');
}
sb.AppendIfNotEmpty(' ');
sb.Append(text);
}
}

36
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs

@ -217,5 +217,41 @@ namespace Squidex.Infrastructure.MongoDb
return result;
}
public static async Task<List<T>> ToListRandomAsync<T>(this IFindFluent<T, T> find, IMongoCollection<T> collection, long take,
CancellationToken ct = default)
{
if (take <= 0)
{
return await find.ToListAsync(ct);
}
var idDocuments = await find.Project<BsonDocument>(Builders<T>.Projection.Include("_id")).ToListAsync(ct);
var idValues = idDocuments.Select(x => x["_id"]);
var randomIds = idValues.TakeRandom(take);
var documents = await collection.Find(Builders<T>.Filter.In("_id", randomIds)).ToListAsync(ct);
return documents.Shuffle().ToList();
}
public static async Task<List<T>> ToListRandomAsync<T>(this IAggregateFluent<T> find, IMongoCollection<T> collection, long take,
CancellationToken ct = default)
{
if (take <= 0)
{
return await find.ToListAsync(ct);
}
var idDocuments = await find.Project<BsonDocument>(Builders<T>.Projection.Include("_id")).ToListAsync(ct);
var idValues = idDocuments.Select(x => x["_id"]);
var randomIds = idValues.TakeRandom(take);
var documents = await collection.Find(Builders<T>.Filter.In("_id", randomIds)).ToListAsync(ct);
return documents.Shuffle().ToList();
}
}
}

49
backend/src/Squidex.Infrastructure/CollectionExtensions.cs

@ -98,11 +98,7 @@ namespace Squidex.Infrastructure
foreach (var item in source)
{
if (bucket == null)
{
bucket = new TSource[size];
}
bucket ??= new TSource[size];
bucket[bucketIndex++] = item;
if (bucketIndex != size)
@ -179,9 +175,7 @@ namespace Squidex.Infrastructure
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
{
var random = new Random();
return enumerable.OrderBy(x => random.Next()).ToList();
return enumerable.OrderBy(x => Random.Shared.Next()).ToList();
}
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T>? source)
@ -389,5 +383,44 @@ namespace Squidex.Infrastructure
index++;
}
}
public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> source, long take)
{
var sourceList = new LinkedList<T>(source);
static LinkedListNode<T> TakeNode(LinkedList<T> source, int index)
{
var actual = source.First!;
for (var i = 0; i < index; i++)
{
var next = actual?.Next;
if (next == null)
{
return actual!;
}
actual = next;
}
return actual;
}
for (var i = 0; i < take; i++)
{
if (sourceList.Count == 0)
{
break;
}
var takenIndex = Random.Shared.Next(sourceList.Count);
var takenElement = TakeNode(sourceList, takenIndex);
sourceList.Remove(takenElement);
yield return takenElement.Value;
}
}
}
}

1
backend/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs

@ -69,6 +69,7 @@ namespace Squidex.Infrastructure.Queries.OData
parser.ParseSkip(query);
parser.ParseFilter(query);
parser.ParseSort(query);
parser.ParseRandom(query);
}
return query;

17
backend/src/Squidex.Infrastructure/Queries/OData/LimitExtensions.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Globalization;
using Microsoft.OData.UriParser;
namespace Squidex.Infrastructure.Queries.OData
@ -30,5 +31,21 @@ namespace Squidex.Infrastructure.Queries.OData
result.Skip = skip.Value;
}
}
public static void ParseRandom(this ODataUriParser query, ClrQuery result)
{
var customQueries = query.CustomQueryOptions;
var randomQuery = customQueries.FirstOrDefault(x =>
string.Equals(x.Key, "random", StringComparison.OrdinalIgnoreCase) ||
string.Equals(x.Key, "randomCount", StringComparison.OrdinalIgnoreCase) ||
string.Equals(x.Key, "$random", StringComparison.OrdinalIgnoreCase) ||
string.Equals(x.Key, "$randomCount", StringComparison.OrdinalIgnoreCase));
if (int.TryParse(randomQuery.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var random))
{
result.Random = random;
}
}
}
}

29
backend/src/Squidex.Infrastructure/Queries/Query.cs

@ -5,6 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Text;
namespace Squidex.Infrastructure.Queries
{
public class Query<TValue>
@ -17,6 +19,8 @@ namespace Squidex.Infrastructure.Queries
public long Take { get; set; } = long.MaxValue;
public long Random { get; set; }
public long Top
{
set => Take = value;
@ -43,34 +47,45 @@ namespace Squidex.Infrastructure.Queries
public override string ToString()
{
var parts = new List<string>();
var sb = new StringBuilder();
if (Filter != null)
{
parts.Add($"Filter: {Filter}");
sb.AppendIfNotEmpty("; ");
sb.Append($"Filter: {Filter}");
}
if (FullText != null)
{
parts.Add($"FullText: '{FullText.Replace("'", "\'", StringComparison.Ordinal)}'");
sb.AppendIfNotEmpty("; ");
sb.Append($"FullText: '{FullText.Replace("'", "\'", StringComparison.Ordinal)}'");
}
if (Skip > 0)
{
parts.Add($"Skip: {Skip}");
sb.AppendIfNotEmpty("; ");
sb.Append($"Skip: {Skip}");
}
if (Take < long.MaxValue)
{
parts.Add($"Take: {Take}");
sb.AppendIfNotEmpty("; ");
sb.Append($"Take: {Take}");
}
if (Random > 0)
{
sb.AppendIfNotEmpty("; ");
sb.Append($"Random: {Random}");
}
if (Sort != null && Sort.Count > 0)
{
parts.Add($"Sort: {string.Join(", ", Sort)}");
sb.AppendIfNotEmpty("; ");
sb.Append($"Sort: {string.Join(", ", Sort)}");
}
return string.Join("; ", parts);
return sb.ToString();
}
}
}

21
backend/src/Squidex.Infrastructure/StringExtensions.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System.Globalization;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.RegularExpressions;
@ -114,5 +115,25 @@ namespace Squidex.Infrastructure
return new string(span);
}
public static StringBuilder AppendIfNotEmpty(this StringBuilder sb, char separator)
{
if (sb.Length > 0)
{
sb.Append(separator);
}
return sb;
}
public static StringBuilder AppendIfNotEmpty(this StringBuilder sb, string separator)
{
if (sb.Length > 0)
{
sb.Append(separator);
}
return sb;
}
}
}

6
backend/src/Squidex.Web/ExposedValues.cs

@ -48,11 +48,7 @@ namespace Squidex.Web
foreach (var (key, value) in this)
{
if (sb.Length > 0)
{
sb.Append(", ");
}
sb.AppendIfNotEmpty(", ");
sb.Append(key);
sb.Append(": ");
sb.Append(value);

7
backend/src/Squidex/Pipeline/Squid/SquidMiddleware.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
using System.Text;
namespace Squidex.Pipeline.Squid
@ -97,11 +98,7 @@ namespace Squidex.Pipeline.Squid
line.Clear();
}
if (line.Length > 0)
{
line.Append(' ');
}
line.AppendIfNotEmpty(' ');
line.Append(word);
}

1
backend/src/Squidex/Squidex.csproj

@ -54,7 +54,6 @@
<PackageReference Include="Microsoft.IdentityModel.Logging" Version="6.14.1" />
<PackageReference Include="Microsoft.IdentityModel.Protocols" Version="6.14.1" />
<PackageReference Include="Microsoft.OData.Core" Version="7.11.0" />
<PackageReference Include="Namotion.Reflection" Version="2.0.10" />
<PackageReference Include="MongoDB.Driver" Version="2.17.1" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.OpenTelemetry" Version="1.0.0" />
<PackageReference Include="Namotion.Reflection" Version="2.1.1" ExcludeAssets="all" />

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

@ -24,7 +24,6 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
{
public sealed class AssetsQueryFixture : IAsyncLifetime
{
private readonly Random random = new Random();
private readonly int numValues = 250;
private readonly IMongoClient mongoClient;
private readonly IMongoDatabase mongoDatabase;
@ -103,8 +102,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var asset = new AssetDomainObject.State
{
Tags = new HashSet<string> { tag },
Id = DomainId.NewGuid(),
AppId = appId,
Created = now,
CreatedBy = user,
FileHash = fileName,
@ -118,6 +117,10 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
{
["value"] = JsonValue.Create(tag)
},
Tags = new HashSet<string>
{
tag
},
Slug = fileName
};
@ -139,12 +142,12 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
public DomainId RandomAppId()
{
return AppIds[random.Next(0, AppIds.Length)].Id;
return AppIds[Random.Shared.Next(AppIds.Length)].Id;
}
public string RandomValue()
{
return random.Next(0, numValues).ToString(CultureInfo.InvariantCulture);
return Random.Shared.Next(numValues).ToString(CultureInfo.InvariantCulture);
}
}
}

35
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryIntegrationTests.cs

@ -31,6 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var asset = await _.AssetRepository.FindAssetBySlugAsync(_.RandomAppId(), random);
// The Slug is random here, as it does not really matter.
Assert.NotNull(asset);
}
@ -41,6 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await _.AssetRepository.FindAssetByHashAsync(_.RandomAppId(), random, random, 1024);
// The Hash is random here, as it does not really matter.
Assert.NotNull(assets);
}
@ -51,18 +53,35 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await _.AssetRepository.QueryIdsAsync(_.RandomAppId(), ids);
// The IDs are random here, as it does not really matter.
Assert.NotNull(assets);
}
[Theory]
[MemberData(nameof(ParentIds))]
public async Task Should_query_assets_by_default(DomainId? parentId)
public async Task Should_query_assets(DomainId? parentId)
{
var query = new ClrQuery();
var assets = await QueryAsync(parentId, query);
Assert.NotNull(assets);
// Default page size is 1000.
Assert.Equal(1000, assets.Count);
}
[Theory]
[MemberData(nameof(ParentIds))]
public async Task Should_query_assets_with_random_count(DomainId? parentId)
{
var query = new ClrQuery
{
Random = 40
};
var assets = await QueryAsync(parentId, query);
// Default page size is 1000, so we expect less elements.
Assert.Equal(40, assets.Count);
}
[Theory]
@ -78,12 +97,13 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await QueryAsync(parentId, query);
// The tag is random here, as it does not really matter.
Assert.NotNull(assets);
}
[Theory]
[MemberData(nameof(ParentIds))]
public async Task Should_query_assets_by_tags_and_name(DomainId? parentId)
public async Task Should_query_assets_by_tags_and_fileName(DomainId? parentId)
{
var random = _.RandomValue();
@ -94,7 +114,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await QueryAsync(parentId, query);
Assert.NotNull(assets);
// The filter is a random value from the expected result set.
Assert.NotEmpty(assets);
}
[Theory]
@ -110,7 +131,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await QueryAsync(parentId, query);
Assert.NotNull(assets);
// The filter is a random value from the expected result set.
Assert.NotEmpty(assets);
}
[Theory]
@ -126,7 +148,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
var assets = await QueryAsync(parentId, query);
Assert.NotNull(assets);
// The filter is a random value from the expected result set.
Assert.NotEmpty(assets);
}
public static IEnumerable<object?[]> ParentIds()

3
backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs

@ -163,7 +163,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
[InlineData(BackupVersion.V2)]
public async Task Should_write_and_read_events_to_backup(BackupVersion version)
{
var randomGenerator = new Random();
var randomDomainIds = new List<DomainId>();
for (var i = 0; i < 100; i++)
@ -173,7 +172,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
DomainId RandomDomainId()
{
return randomDomainIds[randomGenerator.Next(randomDomainIds.Count)];
return randomDomainIds[Random.Shared.Next(randomDomainIds.Count)];
}
var sourceEvents = new List<(string Stream, Envelope<MyEvent> Event)>();

23
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryDedicatedIntegrationTests.cs

@ -0,0 +1,23 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Xunit;
#pragma warning disable SA1300 // Element should begin with upper-case letter
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{
[Trait("Category", "Dependencies")]
public class ContentsQueryDedicatedIntegrationTests : ContentsQueryTestsBase, IClassFixture<ContentsQueryDedicatedFixture>
{
public ContentsQueryDedicatedIntegrationTests(ContentsQueryDedicatedFixture fixture)
: base(fixture)
{
}
}
}

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

@ -49,7 +49,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public abstract class ContentsQueryFixtureBase : IAsyncLifetime
{
private readonly Random random = new Random();
private readonly int numValues = 10000;
private readonly IMongoClient mongoClient;
private readonly IMongoDatabase mongoDatabase;
@ -196,15 +195,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{
var appProvider = A.Fake<IAppProvider>();
A.CallTo(() => appProvider.GetSchemaAsync(A<DomainId>._, A<DomainId>._, false, A<CancellationToken>._))
.ReturnsLazily(x => Task.FromResult<ISchemaEntity?>(CreateSchema(x.GetArgument<DomainId>(0)!, x.GetArgument<DomainId>(1)!)));
A.CallTo(() => appProvider.GetAppWithSchemaAsync(A<DomainId>._, A<DomainId>._, false, A<CancellationToken>._))
.ReturnsLazily(x =>
{
var appId = x.GetArgument<DomainId>(0)!;
return Task.FromResult<(IAppEntity?, ISchemaEntity?)>((
CreateApp(appId),
CreateSchema(appId, x.GetArgument<DomainId>(1)!)));
});
return appProvider;
}
public DomainId RandomAppId()
{
return AppIds[random.Next(0, AppIds.Length)].Id;
return AppIds[Random.Shared.Next(AppIds.Length)].Id;
}
public IAppEntity RandomApp()
@ -214,7 +220,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public DomainId RandomSchemaId()
{
return SchemaIds[random.Next(0, SchemaIds.Length)].Id;
return SchemaIds[Random.Shared.Next(SchemaIds.Length)].Id;
}
public ISchemaEntity RandomSchema()
@ -224,7 +230,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public string RandomValue()
{
return random.Next(0, numValues).ToString(CultureInfo.InvariantCulture);
return Random.Shared.Next(numValues).ToString(CultureInfo.InvariantCulture);
}
private static IAppEntity CreateApp(DomainId appId)

219
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryIntegrationTests.cs

@ -5,16 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NodaTime;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Queries;
using Xunit;
using F = Squidex.Infrastructure.Queries.ClrFilter;
#pragma warning disable SA1300 // Element should begin with upper-case letter
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{
@ -26,214 +17,4 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{
}
}
[Trait("Category", "Dependencies")]
public class ContentsQueryDedicatedIntegrationTests : ContentsQueryTestsBase, IClassFixture<ContentsQueryDedicatedFixture>
{
public ContentsQueryDedicatedIntegrationTests(ContentsQueryDedicatedFixture fixture)
: base(fixture)
{
}
}
public abstract class ContentsQueryTestsBase
{
public ContentsQueryFixtureBase _ { get; }
protected ContentsQueryTestsBase(ContentsQueryFixtureBase fixture)
{
_ = fixture;
}
[Fact]
public async Task Should_verify_ids()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), ids, SearchScope.Published);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var schemas = new List<ISchemaEntity>
{
_.RandomSchema()
};
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), schemas, Q.Empty.WithIds(ids), SearchScope.All);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids_and_schema()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithIds(ids), SearchScope.All);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_ids_by_filter()
{
var filter = F.Eq("data.field1.iv", 12);
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), _.RandomSchemaId(), filter);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_by_filter()
{
var query = new ClrQuery
{
Filter = F.Eq("data.field1.iv", 12)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_scheduled()
{
var time = SystemClock.Instance.GetCurrentInstant();
var contents = await _.ContentRepository.QueryScheduledWithoutDataAsync(time).ToListAsync();
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query()
{
var query = new ClrQuery();
var contents = await QueryAsync(_.ContentRepository, query);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query_and_id()
{
var query = new ClrQuery();
var contents = await QueryAsync(_.ContentRepository, query, reference: DomainId.NewGuid());
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_large_skip()
{
var query = new ClrQuery
{
Sort = new List<SortNode>
{
new SortNode("data.value.iv", SortOrder.Ascending)
}
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 9000);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_fulltext()
{
var query = new ClrQuery
{
FullText = "hello"
};
var contents = await QueryAsync(_.ContentRepository, query);
// The full text is resolved by another system, so we cannot verify the actual.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter()
{
var query = new ClrQuery
{
Filter = F.Eq("data.field1.iv", 200)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter_and_id()
{
var query = new ClrQuery
{
Filter = F.Eq("data.value.iv", 12)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0, reference: DomainId.NewGuid());
// We do not insert test entities with references, so we cannot verify the actual.
Assert.Empty(contents);
}
private async Task<IResultList<IContentEntity>> QueryAsync(IContentRepository contentRepository,
ClrQuery clrQuery,
int take = 1000,
int skip = 100,
DomainId reference = default)
{
if (clrQuery.Take == long.MaxValue)
{
clrQuery.Take = take;
}
if (clrQuery.Skip == 0)
{
clrQuery.Skip = skip;
}
if (clrQuery.Sort == null || clrQuery.Sort.Count == 0)
{
clrQuery.Sort = new List<SortNode>
{
new SortNode("LastModified", SortOrder.Descending),
new SortNode("Id", SortOrder.Ascending)
};
}
var q =
Q.Empty
.WithoutTotal()
.WithQuery(clrQuery)
.WithReference(reference);
var contents = await contentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All);
return contents;
}
}
}

235
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryTestsBase.cs

@ -0,0 +1,235 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NodaTime;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Queries;
using Xunit;
using F = Squidex.Infrastructure.Queries.ClrFilter;
#pragma warning disable SA1300 // Element should begin with upper-case letter
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{
public abstract class ContentsQueryTestsBase
{
public ContentsQueryFixtureBase _ { get; }
protected ContentsQueryTestsBase(ContentsQueryFixtureBase fixture)
{
_ = fixture;
}
[Fact]
public async Task Should_verify_ids()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), ids, SearchScope.Published);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var schemas = new List<ISchemaEntity>
{
_.RandomSchema()
};
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), schemas, Q.Empty.WithIds(ids), SearchScope.All);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids_and_schema()
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithIds(ids), SearchScope.All);
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_ids_by_filter()
{
var filter = F.Eq("data.field1.iv", 12);
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), _.RandomSchemaId(), filter);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_by_filter()
{
var query = new ClrQuery
{
Filter = F.Eq("data.field1.iv", 12)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0);
// We have a concrete query, so we expect an actual.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_scheduled()
{
var time = SystemClock.Instance.GetCurrentInstant();
var contents = await _.ContentRepository.QueryScheduledWithoutDataAsync(time).ToListAsync();
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query()
{
var query = new ClrQuery();
var contents = await QueryAsync(_.ContentRepository, query);
// We have a concrete query, so we expect an actual result.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query_and_id()
{
var query = new ClrQuery();
var contents = await QueryAsync(_.ContentRepository, query, reference: DomainId.NewGuid());
// The IDs are random here, as it does not really matter.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_large_skip()
{
var query = new ClrQuery
{
Sort = new List<SortNode>
{
new SortNode("data.value.iv", SortOrder.Ascending)
}
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 9000);
// We have a concrete query, so we expect an actual result.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_fulltext()
{
var query = new ClrQuery
{
FullText = "hello"
};
var contents = await QueryAsync(_.ContentRepository, query);
// The full text is resolved by another system, so we cannot verify the actual result.
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter()
{
var query = new ClrQuery
{
Filter = F.Eq("data.field1.iv", 200)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0);
// We have a concrete query, so we expect an actual result.
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter_and_id()
{
var query = new ClrQuery
{
Filter = F.Eq("data.value.iv", 12)
};
var contents = await QueryAsync(_.ContentRepository, query, 1000, 0, reference: DomainId.NewGuid());
// We do not insert test entities with references, so we cannot verify the actual result.
Assert.Empty(contents);
}
[Fact]
public async Task Should_query_contents_with_random_count()
{
var query = new ClrQuery
{
Random = 40
};
var contents = await QueryAsync(_.ContentRepository, query);
// We do not insert test entities with references, so we cannot verify the actual.
Assert.Equal(40, contents.Count);
}
private async Task<IResultList<IContentEntity>> QueryAsync(IContentRepository contentRepository,
ClrQuery clrQuery,
int take = 1000,
int skip = 100,
DomainId reference = default)
{
if (clrQuery.Take == long.MaxValue)
{
clrQuery.Take = take;
}
if (clrQuery.Skip == 0)
{
clrQuery.Skip = skip;
}
if (clrQuery.Sort == null || clrQuery.Sort.Count == 0)
{
clrQuery.Sort = new List<SortNode>
{
new SortNode("LastModified", SortOrder.Descending),
new SortNode("Id", SortOrder.Ascending)
};
}
var q =
Q.Empty
.WithoutTotal()
.WithQuery(clrQuery)
.WithReference(reference);
var contents = await contentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All);
return contents;
}
}
}

46
backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs

@ -576,29 +576,61 @@ namespace Squidex.Infrastructure.Queries
}
[Fact]
public void Should_parse_query()
public void Should_parse_filter()
{
var json = new { skip = 10, take = 20, FullText = "Hello", Filter = new { path = "string", op = "eq", value = "Hello" } };
var json = new { Filter = new { path = "string", op = "eq", value = "Hello" } };
AssertQuery(json, "Filter: string == 'Hello'; FullText: 'Hello'; Skip: 10; Take: 20");
AssertQuery(json, "Filter: string == 'Hello'");
}
[Fact]
public void Should_parse_query_with_top()
public void Should_parse_fulltext()
{
var json = new { skip = 10, top = 20, FullText = "Hello", Filter = new { path = "string", op = "eq", value = "Hello" } };
var json = new { FullText = "Hello" };
AssertQuery(json, "Filter: string == 'Hello'; FullText: 'Hello'; Skip: 10; Take: 20");
AssertQuery(json, "FullText: 'Hello'");
}
[Fact]
public void Should_parse_query_with_sorting()
public void Should_parse_sort()
{
var json = new { sort = new[] { new { path = "string", order = "ascending" } } };
AssertQuery(json, "Sort: string Ascending");
}
[Fact]
public void Should_parse_top()
{
var json = new { top = 20 };
AssertQuery(json, "Take: 20");
}
[Fact]
public void Should_parse_take()
{
var json = new { take = 20 };
AssertQuery(json, "Take: 20");
}
[Fact]
public void Should_parse_skip()
{
var json = new { skip = 10 };
AssertQuery(json, "Skip: 10");
}
[Fact]
public void Should_parse_random()
{
var json = new { random = 4 };
AssertQuery(json, "Random: 4");
}
[Fact]
public void Should_throw_exception_for_invalid_query()
{

46
backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs

@ -442,34 +442,34 @@ namespace Squidex.Infrastructure.Queries
}
[Fact]
public void Should_parse_filter_with_full_text_numbers()
public void Should_full_text()
{
var i = _Q("$search=\"33k\"");
var o = _C("FullText: '33k'");
var i = _Q("$search=Duck");
var o = _C("FullText: 'Duck'");
Assert.Equal(o, i);
}
[Fact]
public void Should_parse_filter_with_full_text()
public void Should_text_and_multiple_terms()
{
var i = _Q("$search=Duck");
var o = _C("FullText: 'Duck'");
var i = _Q("$search=Dagobert or Donald");
var o = _C("FullText: 'Dagobert or Donald'");
Assert.Equal(o, i);
}
[Fact]
public void Should_parse_filter_with_full_text_and_multiple_terms()
public void Should_full_text_numbers()
{
var i = _Q("$search=Dagobert or Donald");
var o = _C("FullText: 'Dagobert or Donald'");
var i = _Q("$search=\"33k\"");
var o = _C("FullText: '33k'");
Assert.Equal(o, i);
}
[Fact]
public void Should_make_orderby_with_single_field()
public void Should_parse_orderby()
{
var i = _Q("$orderby=age desc");
var o = _C("Sort: age Descending");
@ -478,7 +478,7 @@ namespace Squidex.Infrastructure.Queries
}
[Fact]
public void Should_make_orderby_with_multiple_field()
public void Should_parse_orderby_with_multiple_field()
{
var i = _Q("$orderby=age, incomeMio desc");
var o = _C("Sort: age Ascending, incomeMio Descending");
@ -487,10 +487,28 @@ namespace Squidex.Infrastructure.Queries
}
[Fact]
public void Should_parse_filter_and_take()
public void Should_parse_top()
{
var i = _Q("$top=3");
var o = _C("Take: 3");
Assert.Equal(o, i);
}
[Fact]
public void Should_parse_skip()
{
var i = _Q("$skip=4");
var o = _C("Skip: 4");
Assert.Equal(o, i);
}
[Fact]
public void Should_parse_random()
{
var i = _Q("$top=3&$skip=4");
var o = _C("Skip: 4; Take: 3");
var i = _Q("$random=4");
var o = _C("Random: 4");
Assert.Equal(o, i);
}

4
backend/tests/Squidex.Infrastructure.Tests/Tasks/PartitionedActionBlockTests.cs

@ -17,8 +17,6 @@ namespace Squidex.Infrastructure.Tasks
[Fact]
public async Task Should_propagate_in_order()
{
var random = new Random();
var lists = new List<int>[Partitions];
for (var i = 0; i < Partitions; i++)
@ -28,7 +26,7 @@ namespace Squidex.Infrastructure.Tasks
var block = new PartitionedActionBlock<(int P, int V)>(x =>
{
random.Next(10);
Random.Shared.Next(10);
lists[x.P].Add(x.V);

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

@ -46,14 +46,15 @@ namespace TestSuite.ApiTests
{
var q = new ContentQuery { OrderBy = "data/number/iv asc" };
var itemsByQ = await _.Contents.GetAsync(q);
var itemsIds = itemsByQ.Items.Take(3).Select(x => x.Id).ToHashSet();
var itemsById = await _.Contents.GetAsync(new ContentQuery { Ids = itemsIds });
var items_0 = await _.Contents.GetAsync(q);
var itemsIds = items_0.Items.Take(3).Select(x => x.Id).ToHashSet();
Assert.Equal(3, itemsById.Items.Count);
Assert.Equal(3, itemsById.Total);
var items_1 = await _.Contents.GetAsync(new ContentQuery { Ids = itemsIds });
foreach (var item in itemsById.Items)
Assert.Equal(3, items_1.Items.Count);
Assert.Equal(3, items_1.Total);
foreach (var item in items_1.Items)
{
Assert.Equal(_.AppName, item.AppName);
Assert.Equal(_.SchemaName, item.SchemaName);
@ -65,14 +66,15 @@ namespace TestSuite.ApiTests
{
var q = new ContentQuery { OrderBy = "data/number/iv asc" };
var itemsByQ = await _.Contents.GetAsync(q);
var itemsIds = itemsByQ.Items.Take(3).Select(x => x.Id).ToHashSet();
var itemsById = await _.SharedContents.GetAsync(itemsIds);
var items_0 = await _.Contents.GetAsync(q);
var itemsIds = items_0.Items.Take(3).Select(x => x.Id).ToHashSet();
var items_1 = await _.SharedContents.GetAsync(itemsIds);
Assert.Equal(3, itemsById.Items.Count);
Assert.Equal(3, itemsById.Total);
Assert.Equal(3, items_1.Items.Count);
Assert.Equal(3, items_1.Total);
foreach (var item in itemsById.Items)
foreach (var item in items_1.Items)
{
Assert.Equal(_.AppName, item.AppName);
Assert.Equal(_.SchemaName, item.SchemaName);
@ -80,7 +82,43 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_all_with_odata()
public async Task Should_query_by_ids_filter()
{
var q0 = new ContentQuery { Filter = "data/number/iv gt 3 and data/number/iv lt 7", OrderBy = "data/number/iv asc" };
var items_0 = await _.Contents.GetAsync(q0);
var q1 = new ContentQuery
{
JsonQuery = new
{
sort = new[]
{
new
{
path = "data.number.iv"
}
},
filter = new
{
or = items_0.Items.Select(x => new
{
path = "id",
op = "eq",
value = x.Id
}).ToArray()
}
}
};
var items_1 = await _.Contents.GetAsync(q1);
AssertItems(items_0, 3, new[] { 4, 5, 6 });
AssertItems(items_1, 3, new[] { 4, 5, 6 });
}
[Fact]
public async Task Should_query_all_with_odata()
{
var q = new ContentQuery { OrderBy = "data/number/iv asc" };
@ -90,7 +128,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_all_with_json()
public async Task Should_query_all_with_json()
{
var q = new ContentQuery
{
@ -112,7 +150,33 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_skip_with_odata()
public async Task Should_query_random_with_odata()
{
var q = new ContentQuery { Random = 5 };
var items = await _.Contents.GetAsync(q);
Assert.Equal(5, items.Items.Count);
}
[Fact]
public async Task Should_query_random_with_json()
{
var q = new ContentQuery
{
JsonQuery = new
{
random = 5
}
};
var items = await _.Contents.GetAsync(q);
Assert.Equal(5, items.Items.Count);
}
[Fact]
public async Task Should_query_by_skip_with_odata()
{
var q = new ContentQuery { OrderBy = "data/number/iv asc", Skip = 5 };
@ -122,7 +186,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_skip_with_json()
public async Task Should_query_by_skip_with_json()
{
var q = new ContentQuery
{
@ -145,7 +209,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_skip_and_top_with_odata()
public async Task Should_query_by_skip_and_top_with_odata()
{
var q = new ContentQuery { Skip = 2, Top = 5, OrderBy = "data/number/iv asc" };
@ -155,7 +219,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_skip_and_top_with_json()
public async Task Should_query_by_skip_and_top_with_json()
{
var q = new ContentQuery
{
@ -179,7 +243,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_filter_with_odata()
public async Task Should_query_by_filter_with_odata()
{
var q = new ContentQuery { Filter = "data/number/iv gt 3 and data/number/iv lt 7", OrderBy = "data/number/iv asc" };
@ -189,7 +253,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_filter_with_json()
public async Task Should_query_by_filter_with_json()
{
var q = new ContentQuery
{
@ -229,7 +293,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_json_filter_with_json()
public async Task Should_query_by_json_filter_with_json()
{
var q = new ContentQuery
{
@ -269,7 +333,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_full_text_with_odata()
public async Task Should_query_by_full_text_with_odata()
{
var q = new ContentQuery { Search = "2" };
@ -279,7 +343,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_full_text_with_json()
public async Task Should_query_by_full_text_with_json()
{
var q = new ContentQuery
{
@ -295,7 +359,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_near_location_with_odata()
public async Task Should_query_by_near_location_with_odata()
{
var q = new ContentQuery { Filter = "geo.distance(data/geo/iv, geography'POINT(3 3)') lt 1000" };
@ -305,7 +369,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_near_location_with_json()
public async Task Should_query_by_near_location_with_json()
{
var q = new ContentQuery
{
@ -331,7 +395,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_near_geoson_location_with_odata()
public async Task Should_query_by_near_geoson_location_with_odata()
{
var q = new ContentQuery { Filter = "geo.distance(data/geo/iv, geography'POINT(4 4)') lt 1000" };
@ -390,7 +454,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_items_by_near_geoson_location_with_json()
public async Task Should_query_by_near_geoson_location_with_json()
{
var q = new ContentQuery
{
@ -444,42 +508,6 @@ namespace TestSuite.ApiTests
Assert.Equal(555, value);
}
[Fact]
public async Task Should_return_items_by_ids()
{
var q0 = new ContentQuery { Filter = "data/number/iv gt 3 and data/number/iv lt 7", OrderBy = "data/number/iv asc" };
var items_0 = await _.Contents.GetAsync(q0);
var q1 = new ContentQuery
{
JsonQuery = new
{
sort = new[]
{
new
{
path = "data.number.iv"
}
},
filter = new
{
or = items_0.Items.Select(x => new
{
path = "id",
op = "eq",
value = x.Id
}).ToArray()
}
}
};
var items_1 = await _.Contents.GetAsync(q1);
AssertItems(items_0, 3, new[] { 4, 5, 6 });
AssertItems(items_1, 3, new[] { 4, 5, 6 });
}
[Fact]
public async Task Should_create_and_query_with_variable_graphql()
{
@ -516,7 +544,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_batch_query_items_with_graphql()
public async Task Should_query_with_graphql_batching()
{
var query1 = new
{
@ -566,7 +594,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_query_items_with_graphql()
public async Task Should_query_with_graphql()
{
var query = new
{
@ -594,7 +622,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_query_items_with_graphql_get()
public async Task Should_query_with_graphql_get()
{
var query = new
{
@ -622,7 +650,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_query_items_with_graphql_with_dynamic()
public async Task Should_query_with_graphql_with_dynamic()
{
var query = new
{
@ -646,7 +674,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_query_items_complex_search()
public async Task Should_query_with_grapqhl_complex_search()
{
var query = new
{
@ -671,7 +699,7 @@ namespace TestSuite.ApiTests
}
[Fact]
public async Task Should_return_correct_content_type_for_graphql()
public async Task Should_query_correct_content_type_for_graphql()
{
var query = new
{
@ -694,13 +722,10 @@ namespace TestSuite.ApiTests
using (var client = _.ClientManager.CreateHttpClient())
{
// Create the request manually to check the content type.
var response = await client.PostAsync(_.ClientManager.GenerateUrl($"api/content/{_.AppName}/graphql/batch"), query.ToContent());
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
var result = await response.Content.ReadAsJsonAsync<GraphQlResponse<QueryResult>>();
Assert.Equal(result.Data.Items.Select(x => x.Data.Number).ToArray(), new[] { 4, 5, 6 });
}
}

4
backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

@ -16,8 +16,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.ClientLibrary" Version="11.0.0-beta1" />
<PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="11.0.0-beta1" />
<PackageReference Include="Squidex.ClientLibrary" Version="11.1.0" />
<PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="11.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Verify" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.1" />

Loading…
Cancel
Save