mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
10 KiB
358 lines
10 KiB
// ==========================================================================
|
|
// Squidex Headless CMS
|
|
// ==========================================================================
|
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
// All rights reserved. Licensed under the MIT license.
|
|
// ==========================================================================
|
|
|
|
using FakeItEasy;
|
|
using MongoDB.Bson.Serialization;
|
|
using MongoDB.Driver;
|
|
using NodaTime;
|
|
using NodaTime.Text;
|
|
using Squidex.Infrastructure.MongoDb.Queries;
|
|
using Squidex.Infrastructure.Queries;
|
|
using Xunit;
|
|
using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter;
|
|
using SortBuilder = Squidex.Infrastructure.Queries.SortBuilder;
|
|
|
|
namespace Squidex.Infrastructure.MongoDb
|
|
{
|
|
public class MongoQueryTests
|
|
{
|
|
public class TestEntity
|
|
{
|
|
public DomainId Id { get; set; }
|
|
|
|
public Instant Created { get; set; }
|
|
|
|
public RefToken CreatedBy { get; set; }
|
|
|
|
public string Text { get; set; }
|
|
|
|
public long Version { get; set; }
|
|
}
|
|
|
|
static MongoQueryTests()
|
|
{
|
|
DomainIdSerializer.Register();
|
|
|
|
TypeConverterStringSerializer<RefToken>.Register();
|
|
|
|
InstantSerializer.Register();
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_not_throw_exception_for_invalid_field()
|
|
{
|
|
var filter = ClrFilter.Eq("invalid", "Value");
|
|
|
|
AssertQuery("{ 'invalid' : 'Value' }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_id_guid()
|
|
{
|
|
var id = Guid.NewGuid();
|
|
|
|
var filter = ClrFilter.Eq("Id", id);
|
|
|
|
AssertQuery("{ '_id' : '[value]' }", filter, id);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_id_string()
|
|
{
|
|
var id = DomainId.NewGuid().ToString();
|
|
|
|
var filter = ClrFilter.Eq("Id", id);
|
|
|
|
AssertQuery("{ '_id' : '[value]' }", filter, id);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_id_guid_list()
|
|
{
|
|
var id = Guid.NewGuid();
|
|
|
|
var filter = ClrFilter.In("Id", new List<Guid> { id });
|
|
|
|
AssertQuery("{ '_id' : { '$in' : ['[value]'] } }", filter, id);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_id_string_list()
|
|
{
|
|
var id = DomainId.NewGuid().ToString();
|
|
|
|
var filter = ClrFilter.In("Id", new List<string> { id });
|
|
|
|
AssertQuery("{ '_id' : { '$in' : ['[value]'] } }", filter, id);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_instant()
|
|
{
|
|
var time = "1988-01-19T12:00:00Z";
|
|
|
|
var filter = ClrFilter.Eq("Version", InstantPattern.ExtendedIso.Parse(time).Value);
|
|
|
|
AssertQuery("{ 'Version' : ISODate('[value]') }", filter, time);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_reftoken()
|
|
{
|
|
var filter = ClrFilter.Eq("CreatedBy", "subject:me");
|
|
|
|
AssertQuery("{ 'CreatedBy' : 'subject:me' }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_reftoken_cleanup()
|
|
{
|
|
var filter = ClrFilter.Eq("CreatedBy", "me");
|
|
|
|
AssertQuery("{ 'CreatedBy' : 'subject:me' }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_reftoken_fix()
|
|
{
|
|
var filter = ClrFilter.Eq("CreatedBy", "user:me");
|
|
|
|
AssertQuery("{ 'CreatedBy' : 'subject:me' }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_number()
|
|
{
|
|
var filter = ClrFilter.Eq("Version", 0L);
|
|
|
|
AssertQuery("{ 'Version' : NumberLong(0) }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_number_and_list()
|
|
{
|
|
var filter = ClrFilter.In("Version", new List<long> { 0L, 2L, 5L });
|
|
|
|
AssertQuery("{ 'Version' : { '$in' : [NumberLong(0), NumberLong(2), NumberLong(5)] } }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_contains_and_null_value()
|
|
{
|
|
var filter = ClrFilter.Contains("Text", null!);
|
|
|
|
AssertQuery("{ 'Text' : /null/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_contains()
|
|
{
|
|
var filter = ClrFilter.Contains("Text", "search");
|
|
|
|
AssertQuery("{ 'Text' : /search/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_contains_and_invalid_character()
|
|
{
|
|
var filter = ClrFilter.Contains("Text", "search(");
|
|
|
|
AssertQuery("{ 'Text' : /search\\(/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_endswith_and_null_value()
|
|
{
|
|
var filter = ClrFilter.EndsWith("Text", null!);
|
|
|
|
AssertQuery("{ 'Text' : /null$/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_endswith()
|
|
{
|
|
var filter = ClrFilter.EndsWith("Text", "search");
|
|
|
|
AssertQuery("{ 'Text' : /search$/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_endswithand_invalid_character()
|
|
{
|
|
var filter = ClrFilter.EndsWith("Text", "search(");
|
|
|
|
AssertQuery("{ 'Text' : /search\\($/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_startswith_and_null_value()
|
|
{
|
|
var filter = ClrFilter.StartsWith("Text", null!);
|
|
|
|
AssertQuery("{ 'Text' : /^null/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_startswith()
|
|
{
|
|
var filter = ClrFilter.StartsWith("Text", "search");
|
|
|
|
AssertQuery("{ 'Text' : /^search/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_startswith_and_invalid_character()
|
|
{
|
|
var filter = ClrFilter.StartsWith("Text", "search(");
|
|
|
|
AssertQuery("{ 'Text' : /^search\\(/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_matchs_and_null_value()
|
|
{
|
|
var filter = ClrFilter.Matchs("Text", null!);
|
|
|
|
AssertQuery("{ 'Text' : /null/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_matchs()
|
|
{
|
|
var filter = ClrFilter.Matchs("Text", "^search$");
|
|
|
|
AssertQuery("{ 'Text' : /^search$/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_matchs_and_regex_syntax()
|
|
{
|
|
var filter = ClrFilter.Matchs("Text", "/search/i");
|
|
|
|
AssertQuery("{ 'Text' : /search/i }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_matchs_and_regex_case_sensitive_syntax()
|
|
{
|
|
var filter = ClrFilter.Matchs("Text", "/search/");
|
|
|
|
AssertQuery("{ 'Text' : /search/ }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_empty_for_class()
|
|
{
|
|
var filter = ClrFilter.Empty("Text");
|
|
|
|
AssertQuery("{ '$or' : [{ 'Text' : { '$exists' : false } }, { 'Text' : null }, { 'Text' : '' }, { 'Text' : { '$size' : 0 } }] }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_exists()
|
|
{
|
|
var filter = ClrFilter.Exists("Text");
|
|
|
|
AssertQuery("{ 'Text' : { '$exists' : true, '$ne' : null } }", filter);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_query_with_full_text()
|
|
{
|
|
var query = new ClrQuery { FullText = "Hello my World" };
|
|
|
|
AssertQuery(query, "{ '$text' : { '$search' : 'Hello my World' } }");
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_orderby_with_single_field()
|
|
{
|
|
var sorting = SortBuilder.Descending("Number");
|
|
|
|
AssertSorting("{ 'Number' : -1 }", sorting);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_orderby_with_multiple_fields()
|
|
{
|
|
var sorting1 = SortBuilder.Ascending("Number");
|
|
var sorting2 = SortBuilder.Descending("Text");
|
|
|
|
AssertSorting("{ 'Number' : 1, 'Text' : -1 }", sorting1, sorting2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_take_statement()
|
|
{
|
|
var query = new ClrQuery { Take = 3 };
|
|
|
|
var cursor = A.Fake<IFindFluent<TestEntity, TestEntity>>();
|
|
|
|
cursor.QueryLimit(query);
|
|
|
|
A.CallTo(() => cursor.Limit(3))
|
|
.MustHaveHappened();
|
|
}
|
|
|
|
[Fact]
|
|
public void Should_make_skip_statement()
|
|
{
|
|
var query = new ClrQuery { Skip = 3 };
|
|
|
|
var cursor = A.Fake<IFindFluent<TestEntity, TestEntity>>();
|
|
|
|
cursor.QuerySkip(query);
|
|
|
|
A.CallTo(() => cursor.Skip(3))
|
|
.MustHaveHappened();
|
|
}
|
|
|
|
private static void AssertQuery(string expected, FilterNode<ClrValue> filter, object? arg = null)
|
|
{
|
|
AssertQuery(new ClrQuery { Filter = filter }, expected, arg);
|
|
}
|
|
|
|
private static void AssertQuery(ClrQuery query, string expected, object? arg = null)
|
|
{
|
|
var filter = query.BuildFilter<TestEntity>().Filter!;
|
|
|
|
var rendered =
|
|
filter.Render(
|
|
BsonSerializer.SerializerRegistry.GetSerializer<TestEntity>(),
|
|
BsonSerializer.SerializerRegistry)
|
|
.ToString();
|
|
|
|
Assert.Equal(Cleanup(expected, arg), rendered);
|
|
}
|
|
|
|
private static void AssertSorting(string expected, params SortNode[] sort)
|
|
{
|
|
var cursor = A.Fake<IFindFluent<TestEntity, TestEntity>>();
|
|
|
|
var rendered = string.Empty;
|
|
|
|
A.CallTo(() => cursor.Sort(A<SortDefinition<TestEntity>>._))
|
|
.Invokes((SortDefinition<TestEntity> sortDefinition) =>
|
|
{
|
|
rendered =
|
|
sortDefinition.Render(
|
|
BsonSerializer.SerializerRegistry.GetSerializer<TestEntity>(),
|
|
BsonSerializer.SerializerRegistry)
|
|
.ToString();
|
|
});
|
|
|
|
cursor.QuerySort(new ClrQuery { Sort = sort.ToList() });
|
|
|
|
Assert.Equal(Cleanup(expected), rendered);
|
|
}
|
|
|
|
private static string Cleanup(string filter, object? arg = null)
|
|
{
|
|
return filter.Replace('\'', '"').Replace("[value]", arg?.ToString(), StringComparison.Ordinal);
|
|
}
|
|
}
|
|
}
|
|
|