Browse Source

Merge branch 'master' of github.com:Squidex/squidex

pull/1235/head
Sebastian Stehle 11 months ago
parent
commit
863fb22cc4
  1. 13
      backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository.cs
  2. 4
      backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentCollection.cs
  3. 4
      backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository.cs
  4. 4
      backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoShardedContentRepository.cs
  5. 4
      backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddDefaultValues.cs
  7. 19
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeOtherFields.cs
  8. 3
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs
  9. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs
  10. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  11. 5
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  12. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs
  13. 46
      backend/tests/Squidex.Data.Tests/Shared/ContentRepositoryTests.cs
  14. 35
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  15. 15
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs
  16. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs
  17. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs
  18. 74
      tools/TestSuite/TestSuite.ApiTests/AssetTests.cs
  19. 9
      tools/TestSuite/TestSuite.ApiTests/BackupTests.cs
  20. 46
      tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs
  21. 34
      tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs
  22. 83
      tools/TestSuite/TestSuite.ApiTests/ContentLanguageTests.cs
  23. 44
      tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  24. 245
      tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs
  25. 99
      tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs
  26. 741
      tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  27. 20
      tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs
  28. 45
      tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

13
backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository.cs

@ -27,18 +27,18 @@ public sealed partial class EFContentRepository<TContext, TContentContext>(
{ {
private readonly DynamicTables<TContext, TContentContext> dynamicTables = new DynamicTables<TContext, TContentContext>(dbContextFactory, dbContentContextFactory); private readonly DynamicTables<TContext, TContentContext> dynamicTables = new DynamicTables<TContext, TContentContext>(dbContextFactory, dbContentContextFactory);
public async Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, public async Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, IReadOnlySet<string>? fields, SearchScope scope,
CancellationToken ct = default) CancellationToken ct = default)
{ {
using (Telemetry.Activities.StartActivity("EFContentRepository/FindContentAsync")) using (Telemetry.Activities.StartActivity("EFContentRepository/FindContentAsync"))
{ {
return scope == SearchScope.All ? return scope == SearchScope.All ?
await FindContentAsync<EFContentCompleteEntity>(app.Id, schema.Id, id, ct) : await FindContentAsync<EFContentCompleteEntity>(app.Id, schema.Id, id, fields, ct) :
await FindContentAsync<EFContentPublishedEntity>(app.Id, schema.Id, id, ct); await FindContentAsync<EFContentPublishedEntity>(app.Id, schema.Id, id, fields, ct);
} }
} }
public async Task<Content?> FindContentAsync<T>(DomainId appId, DomainId schemaId, DomainId id, public async Task<Content?> FindContentAsync<T>(DomainId appId, DomainId schemaId, DomainId id, IReadOnlySet<string>? fields,
CancellationToken ct = default) where T : EFContentEntity CancellationToken ct = default) where T : EFContentEntity
{ {
await using var dbContext = await CreateDbContextAsync(ct); await using var dbContext = await CreateDbContextAsync(ct);
@ -49,6 +49,11 @@ public sealed partial class EFContentRepository<TContext, TContentContext>(
.Where(x => x.IndexedSchemaId == schemaId) .Where(x => x.IndexedSchemaId == schemaId)
.FirstOrDefaultAsync(ct); .FirstOrDefaultAsync(ct);
if (fields?.Count > 0)
{
entity?.Data.LimitFields(fields);
}
return entity; return entity;
} }

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

@ -213,12 +213,12 @@ public sealed class MongoContentCollection : MongoRepositoryBase<MongoContentEnt
} }
} }
public async Task<Content?> FindContentAsync(Schema schema, DomainId id, public async Task<Content?> FindContentAsync(Schema schema, DomainId id, IReadOnlySet<string>? fields,
CancellationToken ct) CancellationToken ct)
{ {
using (Telemetry.Activities.StartActivity("MongoContentCollection/FindContentAsync")) using (Telemetry.Activities.StartActivity("MongoContentCollection/FindContentAsync"))
{ {
return await queryBdId.QueryAsync(schema, id, ct); return await queryBdId.QueryAsync(schema, id, fields, ct);
} }
} }

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

@ -106,10 +106,10 @@ public partial class MongoContentRepository(
return GetCollection(scope).QueryAsync(app, schema, q, ct); return GetCollection(scope).QueryAsync(app, schema, q, ct);
} }
public Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, public Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, IReadOnlySet<string>? fields, SearchScope scope,
CancellationToken ct = default) CancellationToken ct = default)
{ {
return GetCollection(scope).FindContentAsync(schema, id, ct); return GetCollection(scope).FindContentAsync(schema, id, fields, ct);
} }
public Task<IReadOnlyList<ContentIdStatus>> QueryIdsAsync(App app, HashSet<DomainId> ids, SearchScope scope, public Task<IReadOnlyList<ContentIdStatus>> QueryIdsAsync(App app, HashSet<DomainId> ids, SearchScope scope,

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

@ -21,10 +21,10 @@ namespace Squidex.Domain.Apps.Entities.Contents;
public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Func<string, MongoContentRepository> factory) public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Func<string, MongoContentRepository> factory)
: ShardedSnapshotStore<MongoContentRepository, WriteContent>(sharding, factory, x => x.AppId.Id), IContentRepository, IDeleter : ShardedSnapshotStore<MongoContentRepository, WriteContent>(sharding, factory, x => x.AppId.Id), IContentRepository, IDeleter
{ {
public Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, public Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, IReadOnlySet<string>? fields, SearchScope scope,
CancellationToken ct = default) CancellationToken ct = default)
{ {
return Shard(app.Id).FindContentAsync(app, schema, id, scope, ct); return Shard(app.Id).FindContentAsync(app, schema, id, fields, scope, ct);
} }
public Task<bool> HasReferrersAsync(App app, DomainId reference, SearchScope scope, public Task<bool> HasReferrersAsync(App app, DomainId reference, SearchScope scope,

4
backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs

@ -14,12 +14,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Operations;
internal sealed class QueryById : OperationBase internal sealed class QueryById : OperationBase
{ {
public async Task<Content?> QueryAsync(Schema schema, DomainId id, public async Task<Content?> QueryAsync(Schema schema, DomainId id, IReadOnlySet<string>? fields,
CancellationToken ct) CancellationToken ct)
{ {
var filter = Filter.Eq(x => x.DocumentId, DomainId.Combine(schema.AppId, id)); var filter = Filter.Eq(x => x.DocumentId, DomainId.Combine(schema.AppId, id));
var contentEntity = await Collection.Find(filter).SelectFields(null).FirstOrDefaultAsync(ct); var contentEntity = await Collection.Find(filter).SelectFields(fields).FirstOrDefaultAsync(ct);
if (contentEntity == null || contentEntity.IndexedSchemaId != schema.Id) if (contentEntity == null || contentEntity.IndexedSchemaId != schema.Id)
{ {
return null; return null;

2
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddDefaultValues.cs

@ -82,7 +82,7 @@ public sealed class AddDefaultValues(PartitionResolver partitionResolver, IClock
private void Enrich(IField field, Dictionary<string, JsonValue> fieldData, string key) private void Enrich(IField field, Dictionary<string, JsonValue> fieldData, string key)
{ {
if (fieldData.TryGetValue(key, out _) || (field.RawProperties.IsRequired && IgnoreRequiredFields)) if (fieldData.ContainsKey(key) || (field.RawProperties.IsRequired && IgnoreRequiredFields))
{ {
return; return;
} }

19
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeOtherFields.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class ExcludeOtherFields(HashSet<string> fieldsToInclude) : IContentFieldConverter
{
public ContentFieldData? ConvertFieldBefore(IRootField field, ContentFieldData source)
{
return fieldsToInclude.Contains(field.Name) ? source : null;
}
}

3
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs

@ -233,7 +233,6 @@ public sealed partial class ScriptingCompleter(IEnumerable<IScriptDescriptor> de
AddObject("ctx", FieldDescriptions.FieldRuleContext, () => AddObject("ctx", FieldDescriptions.FieldRuleContext, () =>
{ {
Add(JsonType.String, "action", Add(JsonType.String, "action",
FieldDescriptions.UserAppRole, ["Create", "Update"]); FieldDescriptions.UserAppRole, ["Create", "Update"]);
}); });
@ -251,7 +250,7 @@ public sealed partial class ScriptingCompleter(IEnumerable<IScriptDescriptor> de
return Build(); return Build();
} }
private IReadOnlyList<ScriptingValue> Build() private List<ScriptingValue> Build()
{ {
return result.Values.OrderBy(x => x.Path).ToList(); return result.Values.OrderBy(x => x.Path).ToList();
} }

2
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs

@ -150,7 +150,7 @@ public static class ValidationExtensions
operation.Components, operation.Components,
operation.Resolve<IJsonSerializer>()) operation.Resolve<IJsonSerializer>())
{ {
PreviousData = previousData PreviousData = previousData,
}; };
var validationContext = new ValidationContext(rootContext).Optimized(optimize).AsPublishing(published); var validationContext = new ValidationContext(rootContext).Optimized(optimize).AsPublishing(published);

9
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs

@ -190,12 +190,7 @@ public sealed class ContentQueryService(
{ {
var schema = await GetSchemaAsync(context, schemaIdOrName, ct); var schema = await GetSchemaAsync(context, schemaIdOrName, ct);
if (schema == null) return schema ?? throw new DomainObjectNotFoundException(schemaIdOrName);
{
throw new DomainObjectNotFoundException(schemaIdOrName);
}
return schema;
} }
public async Task<Schema?> GetSchemaAsync(Context context, string schemaIdOrName, public async Task<Schema?> GetSchemaAsync(Context context, string schemaIdOrName,
@ -277,7 +272,7 @@ public sealed class ContentQueryService(
// Enforce a hard timeout // Enforce a hard timeout
combined.CancelAfter(options.TimeoutFind); combined.CancelAfter(options.TimeoutFind);
return await contentRepository.FindContentAsync(context.App, schema, id, context.Scope(), combined.Token); return await contentRepository.FindContentAsync(context.App, schema, id, context.Fields(), context.Scope(), combined.Token);
} }
} }

5
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs

@ -154,6 +154,11 @@ public sealed class ConvertData(
} }
} }
if (fieldNames?.Count > 0)
{
converter.Add(new ExcludeOtherFields(fieldNames));
}
if (!context.IsFrontendClient || context.ResolveSchemaNames()) if (!context.IsFrontendClient || context.ResolveSchemaNames())
{ {
converter.Add(new AddSchemaNames(components)); converter.Add(new AddSchemaNames(components));

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs

@ -41,7 +41,7 @@ public interface IContentRepository
Task<IReadOnlyList<ContentIdStatus>> QueryIdsAsync(App app, HashSet<DomainId> ids, SearchScope scope, Task<IReadOnlyList<ContentIdStatus>> QueryIdsAsync(App app, HashSet<DomainId> ids, SearchScope scope,
CancellationToken ct = default); CancellationToken ct = default);
Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, Task<Content?> FindContentAsync(App app, Schema schema, DomainId id, IReadOnlySet<string>? fields, SearchScope scope,
CancellationToken ct = default); CancellationToken ct = default);
Task<bool> HasReferrersAsync(App app, DomainId reference, SearchScope scope, Task<bool> HasReferrersAsync(App app, DomainId reference, SearchScope scope,

46
backend/tests/Squidex.Data.Tests/Shared/ContentRepositoryTests.cs

@ -163,6 +163,32 @@ public abstract class ContentRepositoryTests : GivenContext
return sut; return sut;
} }
[Fact]
public async Task Should_find_by_id()
{
var sut = await CreateAndPrepareSutAsync();
var contentId = await sut.StreamAll(app.Id, [schema.Id], default).Select(x => x.Id).FirstOrDefaultAsync();
var content = await sut.FindContentAsync(app, schema, contentId, null, SearchScope.All);
// ID is not predicable, therefore the weak assertion.
Assert.NotNull(content);
}
[Fact]
public async Task Should_find_by_id_with_limited_fields()
{
var sut = await CreateAndPrepareSutAsync();
var contentId = await sut.StreamAll(app.Id, [schema.Id], default).Select(x => x.Id).FirstOrDefaultAsync();
var content = await sut.FindContentAsync(app, schema, contentId, HashSet.Of("field1"), SearchScope.All);
// Only check that the we only go one field.
Assert.NotNull(content);
Assert.Single(content.Data);
Assert.Contains("field1", content.Data);
}
[Fact] [Fact]
public async Task Should_stream_all_with_schema() public async Task Should_stream_all_with_schema()
{ {
@ -170,6 +196,7 @@ public abstract class ContentRepositoryTests : GivenContext
var count = await sut.StreamAll(AppIds[0].Id, [schema.Id], SearchScope.All).CountAsync(); var count = await sut.StreamAll(AppIds[0].Id, [schema.Id], SearchScope.All).CountAsync();
// IDs is not predicable, therefore the weak assertion.
Assert.Equal(NumValues, count); Assert.Equal(NumValues, count);
} }
@ -180,6 +207,7 @@ public abstract class ContentRepositoryTests : GivenContext
var count = await sut.StreamAll(AppIds[0].Id, null, SearchScope.All).CountAsync(); var count = await sut.StreamAll(AppIds[0].Id, null, SearchScope.All).CountAsync();
// IDs is not predicable, therefore the weak assertion.
Assert.Equal(NumValues * SchemaIds.Length, count); Assert.Equal(NumValues * SchemaIds.Length, count);
} }
@ -190,6 +218,7 @@ public abstract class ContentRepositoryTests : GivenContext
var count = await sut.StreamAll(AppIds[0].Id, [], SearchScope.All).CountAsync(); var count = await sut.StreamAll(AppIds[0].Id, [], SearchScope.All).CountAsync();
// IDs is not predicable, therefore the weak assertion.
Assert.Equal(0, count); Assert.Equal(0, count);
} }
@ -288,6 +317,21 @@ public abstract class ContentRepositoryTests : GivenContext
Assert.Equal(NumValues, contents.Count); Assert.Equal(NumValues, contents.Count);
} }
[Fact]
public async Task Should_query_with_fields()
{
var query = new ClrQuery();
var contents = await QueryAsync(query, fields: HashSet.Of("field1"));
// We have a concrete query, so we expect an actual result.
Assert.All(contents, content =>
{
Assert.Single(content.Data);
Assert.Contains("field1", content.Data);
});
}
[Fact] [Fact]
public async Task Should_query_with_large_skip() public async Task Should_query_with_large_skip()
{ {
@ -378,6 +422,7 @@ public abstract class ContentRepositoryTests : GivenContext
int skip = 0, int skip = 0,
DomainId reference = default, DomainId reference = default,
DomainId referencing = default, DomainId referencing = default,
HashSet<string>? fields = null,
bool withTotal = false) bool withTotal = false)
{ {
clrQuery.Take = top; clrQuery.Take = top;
@ -396,6 +441,7 @@ public abstract class ContentRepositoryTests : GivenContext
var q = var q =
Q.Empty Q.Empty
.WithFields(fields)
.WithoutTotal(!withTotal) .WithoutTotal(!withTotal)
.WithQuery(clrQuery) .WithQuery(clrQuery)
.WithReference(reference) .WithReference(reference)

35
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

@ -128,6 +128,41 @@ public class FieldConvertersTests
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
[Fact]
public void Should_remove_non_included_fields()
{
var field1 = Fields.Number(1, "number1", Partitioning.Language);
var field2 = Fields.Number(2, "number2", Partitioning.Language);
var schema =
new Schema { Name = "my-schema" }
.AddField(field1)
.AddField(field2);
var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1))
.AddField(field2.Name,
new ContentFieldData()
.AddLocalized("en", JsonValue.Null)
.AddLocalized("de", 1));
var actual =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ExcludeOtherFields(HashSet.Of(field1.Name)))
.Convert(source);
var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1));
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void Should_not_remove_hidden_fields() public void Should_not_remove_hidden_fields()
{ {

15
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs

@ -177,8 +177,7 @@ public class ScriptingCompleterTests
var actual = sut.FieldRule(dataSchema); var actual = sut.FieldRule(dataSchema);
AssertCompletion(actual, AssertCompletion(actual,
new[] [
{
"ctx", "ctx",
"ctx.action", "ctx.action",
"data", "data",
@ -191,7 +190,7 @@ public class ScriptingCompleterTests
"user.email", "user.email",
"user.id", "user.id",
"user.role", "user.role",
}); ]);
} }
[Fact] [Fact]
@ -200,15 +199,14 @@ public class ScriptingCompleterTests
var actual = sut.PreviewUrl(dataSchema); var actual = sut.PreviewUrl(dataSchema);
AssertCompletion(actual, AssertCompletion(actual,
new[] [
{
"accessToken", "accessToken",
"data", "data",
"data['my-field']", "data['my-field']",
"data['my-field'].iv", "data['my-field'].iv",
"id", "id",
"version", "version",
}); ]);
} }
[Fact] [Fact]
@ -356,8 +354,7 @@ public class ScriptingCompleterTests
private static void AssertUsageTrigger(IReadOnlyList<ScriptingValue> actual) private static void AssertUsageTrigger(IReadOnlyList<ScriptingValue> actual)
{ {
AssertCompletion(actual, AssertCompletion(actual,
new[] [
{
"event", "event",
"event.appId", "event.appId",
"event.appId.id", "event.appId.id",
@ -367,7 +364,7 @@ public class ScriptingCompleterTests
"event.name", "event.name",
"event.timestamp", "event.timestamp",
"event.version", "event.version",
}); ]);
} }
private static void AssertCompletion(IReadOnlyList<ScriptingValue> actual, params string[][] expected) private static void AssertCompletion(IReadOnlyList<ScriptingValue> actual, params string[][] expected)

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs

@ -142,7 +142,7 @@ public static class ValidationTestExtensions
components ?? ResolvedComponents.Empty, components ?? ResolvedComponents.Empty,
TestUtils.DefaultSerializer) TestUtils.DefaultSerializer)
{ {
PreviousData = previousData PreviousData = previousData,
}; };
var context = var context =

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs

@ -22,7 +22,6 @@ public class ContentQueryServiceTests : GivenContext
private readonly IContentEnricher contentEnricher = A.Fake<IContentEnricher>(); private readonly IContentEnricher contentEnricher = A.Fake<IContentEnricher>();
private readonly IContentRepository contentRepository = A.Fake<IContentRepository>(); private readonly IContentRepository contentRepository = A.Fake<IContentRepository>();
private readonly IContentLoader contentVersionLoader = A.Fake<IContentLoader>(); private readonly IContentLoader contentVersionLoader = A.Fake<IContentLoader>();
private readonly ContentData contentData = [];
private readonly ContentQueryParser queryParser = A.Fake<ContentQueryParser>(); private readonly ContentQueryParser queryParser = A.Fake<ContentQueryParser>();
private readonly ContentQueryService sut; private readonly ContentQueryService sut;
@ -59,11 +58,9 @@ public class ContentQueryServiceTests : GivenContext
[Fact] [Fact]
public async Task Should_get_schema_from_guid_string() public async Task Should_get_schema_from_guid_string()
{ {
var input = SchemaId.Id.ToString();
var requestContext = SetupContext(); var requestContext = SetupContext();
var actual = await sut.GetSchemaOrThrowAsync(requestContext, input, CancellationToken); var actual = await sut.GetSchemaOrThrowAsync(requestContext, SchemaId.Id.ToString(), CancellationToken);
Assert.Equal(Schema, actual); Assert.Equal(Schema, actual);
} }
@ -71,11 +68,9 @@ public class ContentQueryServiceTests : GivenContext
[Fact] [Fact]
public async Task Should_get_schema_from_name() public async Task Should_get_schema_from_name()
{ {
var input = SchemaId.Name;
var requestContext = SetupContext(); var requestContext = SetupContext();
var actual = await sut.GetSchemaOrThrowAsync(requestContext, input, CancellationToken); var actual = await sut.GetSchemaOrThrowAsync(requestContext, SchemaId.Name, CancellationToken);
Assert.Equal(Schema, actual); Assert.Equal(Schema, actual);
} }
@ -96,21 +91,19 @@ public class ContentQueryServiceTests : GivenContext
var requestContext = SetupContext(allowSchema: false); var requestContext = SetupContext(allowSchema: false);
var content = CreateContent() as Content; var content = CreateContent() as Content;
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, null, A<SearchScope>._, A<CancellationToken>._))
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, A<SearchScope>._, A<CancellationToken>._))
.Returns(content); .Returns(content);
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken)); await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken));
} }
[Fact] [Fact]
public async Task Should_return_null_if_content_by_id_dannot_be_found() public async Task Should_return_null_if_content_by_id_cannot_be_found()
{ {
var requestContext = SetupContext(); var requestContext = SetupContext();
var content = CreateContent(); var content = CreateContent();
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, A<IReadOnlySet<string>>._, A<SearchScope>._, A<CancellationToken>._))
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, A<SearchScope>._, A<CancellationToken>._))
.Returns<Content?>(null); .Returns<Content?>(null);
var actual = await sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken); var actual = await sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken);
@ -124,8 +117,7 @@ public class ContentQueryServiceTests : GivenContext
var requestContext = SetupContext(); var requestContext = SetupContext();
var content = CreateContent(); var content = CreateContent();
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, SchemaId.Id, A<IReadOnlySet<string>>._, SearchScope.Published, A<CancellationToken>._))
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, SchemaId.Id, SearchScope.Published, A<CancellationToken>._))
.Returns(content); .Returns(content);
var actual = await sut.FindAsync(requestContext, SchemaId.Name, DomainId.Create("_schemaId_"), ct: CancellationToken); var actual = await sut.FindAsync(requestContext, SchemaId.Name, DomainId.Create("_schemaId_"), ct: CancellationToken);
@ -143,8 +135,7 @@ public class ContentQueryServiceTests : GivenContext
var requestContext = SetupContext(isFrontend, isUnpublished: unpublished); var requestContext = SetupContext(isFrontend, isUnpublished: unpublished);
var content = CreateContent(); var content = CreateContent();
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, A<IReadOnlySet<string>>._, scope, A<CancellationToken>._))
A.CallTo(() => contentRepository.FindContentAsync(App, Schema, content.Id, scope, A<CancellationToken>._))
.Returns(content); .Returns(content);
var actual = await sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken); var actual = await sut.FindAsync(requestContext, SchemaId.Name, content.Id, ct: CancellationToken);
@ -158,7 +149,6 @@ public class ContentQueryServiceTests : GivenContext
var requestContext = SetupContext(); var requestContext = SetupContext();
var content = CreateContent(); var content = CreateContent();
A.CallTo(() => contentVersionLoader.GetAsync(AppId.Id, content.Id, 13, A<CancellationToken>._)) A.CallTo(() => contentVersionLoader.GetAsync(AppId.Id, content.Id, 13, A<CancellationToken>._))
.Returns(content); .Returns(content);

74
tools/TestSuite/TestSuite.ApiTests/AssetTests.cs

@ -589,10 +589,11 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 2: Query asset by pixel width. // STEP 2: Query asset by pixel width.
var assets_1 = await _.Client.Assets.GetAssetsAsync(new AssetQuery var assets_1 = await _.Client.Assets.GetAssetsAsync(
{ new AssetQuery
Filter = "metadata/pixelWidth eq 600" {
}); Filter = "metadata/pixelWidth eq 600"
});
Assert.Contains(assets_1.Items, x => x.Id == asset_1.Id); Assert.Contains(assets_1.Items, x => x.Id == asset_1.Id);
@ -600,17 +601,20 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 3: Add custom metadata. // STEP 3: Add custom metadata.
asset_1.Metadata["custom"] = "foo"; asset_1.Metadata["custom"] = "foo";
await _.Client.Assets.PutAssetAsync(asset_1.Id, new AnnotateAssetDto await _.Client.Assets.PutAssetAsync(
{ asset_1.Id,
Metadata = asset_1.Metadata new AnnotateAssetDto
}); {
Metadata = asset_1.Metadata
});
// STEP 4: Query asset by custom metadata. // STEP 4: Query asset by custom metadata.
var assets_2 = await _.Client.Assets.GetAssetsAsync(new AssetQuery var assets_2 = await _.Client.Assets.GetAssetsAsync(
{ new AssetQuery
Filter = "metadata/custom eq 'foo'" {
}); Filter = "metadata/custom eq 'foo'"
});
Assert.Contains(assets_2.Items, x => x.Id == asset_1.Id); Assert.Contains(assets_2.Items, x => x.Id == asset_1.Id);
} }
@ -623,10 +627,11 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 2: Query asset by root folder. // STEP 2: Query asset by root folder.
var assets_1 = await _.Client.Assets.GetAssetsAsync(new AssetQuery var assets_1 = await _.Client.Assets.GetAssetsAsync(
{ new AssetQuery
ParentId = Guid.Empty.ToString() {
}); ParentId = Guid.Empty.ToString()
});
Assert.Contains(assets_1.Items, x => x.Id == asset_1.Id); Assert.Contains(assets_1.Items, x => x.Id == asset_1.Id);
} }
@ -648,10 +653,11 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 2: Query asset by root folder. // STEP 2: Query asset by root folder.
var assets_1 = await _.Client.Assets.GetAssetsAsync(new AssetQuery var assets_1 = await _.Client.Assets.GetAssetsAsync(
{ new AssetQuery
ParentId = folder.Id {
}); ParentId = folder.Id
});
Assert.Single(assets_1.Items, x => x.Id == asset_1.Id); Assert.Single(assets_1.Items, x => x.Id == asset_1.Id);
} }
@ -727,10 +733,11 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 4: Retrieve all deleted items and check if found. // STEP 4: Retrieve all deleted items and check if found.
var deleted = await _.Client.Assets.GetAssetsAsync(new AssetQuery var deleted = await _.Client.Assets.GetAssetsAsync(
{ new AssetQuery
Filter = "isDeleted eq true" {
}); Filter = "isDeleted eq true"
});
var permanent = strategy is ContentStrategies.Deletion.SinglePermanent or ContentStrategies.Deletion.BulkPermanent; var permanent = strategy is ContentStrategies.Deletion.SinglePermanent or ContentStrategies.Deletion.BulkPermanent;
@ -774,18 +781,19 @@ public class AssetTests(CreatedAppFixture fixture) : IClassFixture<CreatedAppFix
// STEP 3: Query and recreate asset. // STEP 3: Query and recreate asset.
var assets = await client.Assets.GetAssetsAsync(new AssetQuery var assets = await client.Assets.GetAssetsAsync(
{ new AssetQuery
Query = new
{ {
filter = new Query = new
{ {
path = "isDeleted", filter = new
op = "eq", {
value = true, path = "isDeleted",
op = "eq",
value = true,
}
} }
} });
});
Assert.NotEmpty(assets.Items); Assert.NotEmpty(assets.Items);

9
tools/TestSuite/TestSuite.ApiTests/BackupTests.cs

@ -134,10 +134,11 @@ public class BackupTests(ClientFixture fixture) : IClassFixture<ClientFixture>
var contents = app.Contents<TestEntity, TestEntityData>(schemaName); var contents = app.Contents<TestEntity, TestEntityData>(schemaName);
await contents.CreateAsync(new TestEntityData await contents.CreateAsync(
{ new TestEntityData
Number = 1 {
}); Number = 1
});
// Upload a test asset // Upload a test asset

46
tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs

@ -30,10 +30,11 @@ public class ContentCleanupTests(CreatedAppFixture fixture) : IClassFixture<Crea
// STEP 2: Create a content for this schema. // STEP 2: Create a content for this schema.
var content_1 = await contents.CreateAsync(new TestEntityData var content_1 = await contents.CreateAsync(
{ new TestEntityData
String = "hello" {
}); String = "hello"
});
Assert.Equal("hello", content_1.Data.String); Assert.Equal("hello", content_1.Data.String);
@ -43,10 +44,12 @@ public class ContentCleanupTests(CreatedAppFixture fixture) : IClassFixture<Crea
// STEP 4: Make any update. // STEP 4: Make any update.
var content_2 = await contents.ChangeStatusAsync(content_1.Id, new ChangeStatus var content_2 = await contents.ChangeStatusAsync(
{ content_1.Id,
Status = "Published" new ChangeStatus
}); {
Status = "Published"
});
// Should not return deleted field. // Should not return deleted field.
Assert.Null(content_2.Data.String); Assert.Null(content_2.Data.String);
@ -62,17 +65,19 @@ public class ContentCleanupTests(CreatedAppFixture fixture) : IClassFixture<Crea
// STEP 2: Create a referenced content. // STEP 2: Create a referenced content.
var contentA_1 = await contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}); References = null
});
// STEP 3: Create a content with a reference. // STEP 3: Create a content with a reference.
var contentB_1 = await contents.CreateAsync(new TestEntityWithReferencesData var contentB_1 = await contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}); References = [contentA_1.Id]
});
// STEP 3: Delete a reference. // STEP 3: Delete a reference.
@ -80,10 +85,11 @@ public class ContentCleanupTests(CreatedAppFixture fixture) : IClassFixture<Crea
// STEP 4: Make any update. // STEP 4: Make any update.
var contentB_2 = await contents.ChangeStatusAsync(contentB_1.Id, new ChangeStatus var contentB_2 = await contents.ChangeStatusAsync(contentB_1.Id,
{ new ChangeStatus
Status = "Published" {
}); Status = "Published"
});
// Should not return deleted field. // Should not return deleted field.
Assert.Empty(contentB_2.Data.References!); Assert.Empty(contentB_2.Data.References!);

34
tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs

@ -57,20 +57,26 @@ public class ContentCollationTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 1: Create content. // STEP 1: Create content.
var contents = _.Client.Contents<SimpleEntity, SimpleEntityData>(schemaName); var contents = _.Client.Contents<SimpleEntity, SimpleEntityData>(schemaName);
await contents.CreateAsync(new SimpleEntityData await contents.CreateAsync(
{ new SimpleEntityData
String = "İstanbul" {
}, ContentCreateOptions.AsPublish); String = "İstanbul"
},
await contents.CreateAsync(new SimpleEntityData ContentCreateOptions.AsPublish);
{
String = "Mersin" await contents.CreateAsync(
}, ContentCreateOptions.AsPublish); new SimpleEntityData
{
await contents.CreateAsync(new SimpleEntityData String = "Mersin"
{ },
String = "Lüleburgaz" ContentCreateOptions.AsPublish);
}, ContentCreateOptions.AsPublish);
await contents.CreateAsync(
new SimpleEntityData
{
String = "Lüleburgaz"
},
ContentCreateOptions.AsPublish);
// STEP 2: Get sorted contents. // STEP 2: Get sorted contents.

83
tools/TestSuite/TestSuite.ApiTests/ContentLanguageTests.cs

@ -21,14 +21,17 @@ public class ContentLanguageTests(ContentFixture fixture) : IClassFixture<Conten
public async Task Should_filter_language() public async Task Should_filter_language()
{ {
// STEP 1: Create content. // STEP 1: Create content.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
{ new TestEntityData
Localized = new Dictionary<string, string?>
{ {
["de"] = "Hallo", Localized = new Dictionary<string, string?>
["en"] = "Hello" {
} ["de"] = "Hallo",
}, ContentCreateOptions.AsPublish, QueryContext.Default.WithLanguages("de")); ["en"] = "Hello"
}
},
ContentCreateOptions.AsPublish,
QueryContext.Default.WithLanguages("de"));
Assert.False(content.Data.Localized.ContainsKey("en")); Assert.False(content.Data.Localized.ContainsKey("en"));
Assert.Equal("Hallo", content.Data.Localized["de"]); Assert.Equal("Hallo", content.Data.Localized["de"]);
@ -41,15 +44,17 @@ public class ContentLanguageTests(ContentFixture fixture) : IClassFixture<Conten
public async Task Should_flatten_language(string code, string expected) public async Task Should_flatten_language(string code, string expected)
{ {
// STEP 1: Create content. // STEP 1: Create content.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
{ new TestEntityData
Localized = new Dictionary<string, string?>
{ {
["de"] = "Hallo", Localized = new Dictionary<string, string?>
["en"] = "Hello", {
["custom"] = "Custom" ["de"] = "Hallo",
} ["en"] = "Hello",
}, ContentCreateOptions.AsPublish); ["custom"] = "Custom"
}
},
ContentCreateOptions.AsPublish);
// STEP 2: Get content. // STEP 2: Get content.
@ -64,34 +69,42 @@ public class ContentLanguageTests(ContentFixture fixture) : IClassFixture<Conten
public async Task Should_provide_etag_based_on_headers() public async Task Should_provide_etag_based_on_headers()
{ {
// STEP 1: Create content. // STEP 1: Create content.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
{ new TestEntityData
Localized = new Dictionary<string, string?>
{ {
["de"] = "Hallo", Localized = new Dictionary<string, string?>
["en"] = "Hello" {
} ["de"] = "Hallo",
}, ContentCreateOptions.AsPublish); ["en"] = "Hello"
}
},
ContentCreateOptions.AsPublish);
// STEP 2: Get content. // STEP 2: Get content.
var (etag1, _) = await GetEtagAsync(content.Id, []); var (etag1, _) = await GetEtagAsync(content.Id, []);
var (etag2, _) = await GetEtagAsync(content.Id, new Dictionary<string, string> var (etag2, _) = await GetEtagAsync(
{ content.Id,
["X-Flatten"] = "1" new Dictionary<string, string>
}); {
["X-Flatten"] = "1"
});
var (etag3, _) = await GetEtagAsync(content.Id, new Dictionary<string, string> var (etag3, _) = await GetEtagAsync(
{ content.Id,
["X-Languages"] = "en" new Dictionary<string, string>
}); {
["X-Languages"] = "en"
});
var (etag4, _) = await GetEtagAsync(content.Id, new Dictionary<string, string> var (etag4, _) = await GetEtagAsync(
{ content.Id,
["X-Languages"] = "en", new Dictionary<string, string>
["X-Flatten"] = "1" {
}); ["X-Languages"] = "en",
["X-Flatten"] = "1"
});
static void AssertValue(string? value, string? not = null) static void AssertValue(string? value, string? not = null)
{ {

44
tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs

@ -40,7 +40,35 @@ public class ContentQueryTests(ContentQueryFixture fixture) : IClassFixture<Cont
var items_0 = await _.Client.DynamicContents(_.SchemaName).GetAsync(context: context); var items_0 = await _.Client.DynamicContents(_.SchemaName).GetAsync(context: context);
Assert.True(items_0.Items.TrueForAll(x => x.Data.Count == 1)); Assert.True(items_0.Items.TrueForAll(x => x.Data.Count == 1 && x.Data.ContainsKey(TestEntityData.StringField)));
}
[Fact]
public async Task Should_find_one()
{
var all = await _.Client.DynamicContents(_.SchemaName).GetAsync();
var content = await _.Client.DynamicContents(_.SchemaName).GetAsync(all.Items[0].Id);
Assert.NotNull(content);
}
[Fact]
public async Task Should_find_one_with_fields()
{
var all = await _.Client.DynamicContents(_.SchemaName).GetAsync();
var context =
QueryContext.Default
.WithFields($"data.{TestEntityData.StringField}");
var content = await _.Client.DynamicContents(_.SchemaName).GetAsync(all.Items[0].Id, context);
Assert.NotNull(content);
Assert.Single(content.Data);
Assert.Contains(TestEntityData.StringField, content.Data);
} }
[Fact] [Fact]
@ -455,13 +483,15 @@ public class ContentQueryTests(ContentQueryFixture fixture) : IClassFixture<Cont
try try
{ {
// STEP 1: Create a content item with a text that caused a bug before. // STEP 1: Create a content item with a text that caused a bug before.
content = await _.Contents.CreateAsync(new TestEntityData content = await _.Contents.CreateAsync(
{ new TestEntityData
Json = new JObject
{ {
["search.field.with.dot"] = 42 Json = new JObject
} {
}, ContentCreateOptions.AsPublish); ["search.field.with.dot"] = 42
}
},
ContentCreateOptions.AsPublish);
// STEP 2: Get the item and ensure that the text is the same. // STEP 2: Get the item and ensure that the text is the same.

245
tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs

@ -21,17 +21,20 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
public async Task Should_not_deliver_unpublished_references() public async Task Should_not_deliver_unpublished_references()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}); References = null
});
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
var contentB_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentB_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}, ContentCreateOptions.AsPublish); References = [contentA_1.Id]
},
ContentCreateOptions.AsPublish);
// STEP 3: Query new item. // STEP 3: Query new item.
@ -41,10 +44,12 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
// STEP 4: Publish reference. // STEP 4: Publish reference.
await _.Contents.ChangeStatusAsync(contentA_1.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
{ contentA_1.Id,
Status = "Published" new ChangeStatus
}); {
Status = "Published"
});
// STEP 5: Query new item again. // STEP 5: Query new item again.
@ -57,17 +62,21 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
public async Task Should_not_delete_when_referenced() public async Task Should_not_delete_when_referenced()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}, ContentCreateOptions.AsPublish); References = null
},
ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
await _.Contents.CreateAsync(new TestEntityWithReferencesData await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}, ContentCreateOptions.AsPublish); References = [contentA_1.Id]
},
ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
@ -87,89 +96,103 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
public async Task Should_not_unpublish_when_referenced() public async Task Should_not_unpublish_when_referenced()
{ {
// STEP 1: Create a published referenced content. // STEP 1: Create a published referenced content.
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}, ContentCreateOptions.AsPublish); References = null
},
ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
await _.Contents.CreateAsync(new TestEntityWithReferencesData await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}, ContentCreateOptions.AsPublish); References = [contentA_1.Id]
},
ContentCreateOptions.AsPublish);
// STEP 3: Try to ThrowsAnyAsync with referrer check. // STEP 3: Try to ThrowsAnyAsync with referrer check.
await Assert.ThrowsAnyAsync<SquidexException>(() => await Assert.ThrowsAnyAsync<SquidexException>(() =>
{ {
return _.Contents.ChangeStatusAsync(contentA_1.Id, new ChangeStatus return _.Contents.ChangeStatusAsync(
{ contentA_1.Id,
Status = "Draft", new ChangeStatus
// Ensure that the flag is true. {
CheckReferrers = true Status = "Draft",
}); // Ensure that the flag is true.
CheckReferrers = true
});
}); });
// STEP 4: Delete without referrer check. // STEP 4: Delete without referrer check.
await _.Contents.ChangeStatusAsync(contentA_1.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
{ contentA_1.Id,
Status = "Draft", new ChangeStatus
// It is the default anyway, just to make it more explicit. {
CheckReferrers = false Status = "Draft",
}); // It is the default anyway, just to make it more explicit.
CheckReferrers = false
});
} }
[Fact] [Fact]
public async Task Should_not_delete_with_bulk_when_referenced() public async Task Should_not_delete_with_bulk_when_referenced()
{ {
// STEP 1: Create a referenced content. // STEP 1: Create a referenced content.
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}, ContentCreateOptions.AsPublish); References = null
},
ContentCreateOptions.AsPublish);
// STEP 2: Create a content with a reference. // STEP 2: Create a content with a reference.
await _.Contents.CreateAsync(new TestEntityWithReferencesData await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}, ContentCreateOptions.AsPublish); References = [contentA_1.Id]
},
ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
var result1 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result1 = await _.Contents.BulkUpdateAsync(
{ new BulkUpdate
Jobs = {
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Id = contentA_1.Id, {
Type = BulkUpdateType.Delete, Id = contentA_1.Id,
Status = "Draft" Type = BulkUpdateType.Delete,
}, Status = "Draft"
], },
CheckReferrers = true ],
}); CheckReferrers = true
});
Assert.NotNull(result1[0].Error); Assert.NotNull(result1[0].Error);
// STEP 4: Delete without referrer check. // STEP 4: Delete without referrer check.
var result2 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result2 = await _.Contents.BulkUpdateAsync(
{ new BulkUpdate
Jobs = {
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Id = contentA_1.Id, {
Type = BulkUpdateType.Delete, Id = contentA_1.Id,
Status = "Draft" Type = BulkUpdateType.Delete,
}, Status = "Draft"
], },
CheckReferrers = false ],
}); CheckReferrers = false
});
Assert.Null(result2[0].Error); Assert.Null(result2[0].Error);
} }
@ -178,51 +201,57 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
public async Task Should_not_unpublish_with_bulk_when_referenced() public async Task Should_not_unpublish_with_bulk_when_referenced()
{ {
// STEP 1: Create a published referenced content. // STEP 1: Create a published referenced content.
var contentA_1 = await _.Contents.CreateAsync(new TestEntityWithReferencesData var contentA_1 = await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = null {
}, ContentCreateOptions.AsPublish); References = null
},
ContentCreateOptions.AsPublish);
// STEP 2: Create a published content with a reference. // STEP 2: Create a published content with a reference.
await _.Contents.CreateAsync(new TestEntityWithReferencesData await _.Contents.CreateAsync(
{ new TestEntityWithReferencesData
References = [contentA_1.Id] {
}, ContentCreateOptions.AsPublish); References = [contentA_1.Id]
},
ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
var result1 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result1 = await _.Contents.BulkUpdateAsync(
{ new BulkUpdate
Jobs = {
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Id = contentA_1.Id, {
Type = BulkUpdateType.ChangeStatus, Id = contentA_1.Id,
Status = "Draft" Type = BulkUpdateType.ChangeStatus,
}, Status = "Draft"
], },
CheckReferrers = true ],
}); CheckReferrers = true
});
Assert.NotNull(result1[0].Error); Assert.NotNull(result1[0].Error);
// STEP 4: Delete without referrer check. // STEP 4: Delete without referrer check.
var result2 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result2 = await _.Contents.BulkUpdateAsync(
{ new BulkUpdate
Jobs = {
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Id = contentA_1.Id, {
Type = BulkUpdateType.ChangeStatus, Id = contentA_1.Id,
Status = "Draft" Type = BulkUpdateType.ChangeStatus,
}, Status = "Draft"
], },
CheckReferrers = false ],
}); CheckReferrers = false
});
Assert.Null(result2[0].Error); Assert.Null(result2[0].Error);
} }

99
tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs

@ -37,10 +37,11 @@ public class ContentScriptingTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 2: Create content. // STEP 2: Create content.
var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName); var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData var content = await contents.CreateAsync(
{ new TestEntityData
Number = 13 {
}); Number = 13
});
Assert.Equal(26, content.Data.Number); Assert.Equal(26, content.Data.Number);
} }
@ -62,10 +63,12 @@ public class ContentScriptingTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 2: Create content. // STEP 2: Create content.
var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName); var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData var content = await contents.CreateAsync(
{ new TestEntityData
Number = 13 {
}, ContentCreateOptions.AsPublish); Number = 13
},
ContentCreateOptions.AsPublish);
Assert.Equal(26, content.Data.Number); Assert.Equal(26, content.Data.Number);
} }
@ -89,10 +92,12 @@ public class ContentScriptingTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 2: Create content. // STEP 2: Create content.
var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName); var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName);
var content = await contents.CreateAsync(new TestEntityData var content = await contents.CreateAsync(
{ new TestEntityData
Number = 99 {
}, ContentCreateOptions.AsPublish); Number = 99
},
ContentCreateOptions.AsPublish);
Assert.Equal(19, content.Data.Number); Assert.Equal(19, content.Data.Number);
} }
@ -114,26 +119,27 @@ public class ContentScriptingTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 2: Create content with a value that triggers the schema. // STEP 2: Create content with a value that triggers the schema.
var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName); var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName);
var results = await contents.BulkUpdateAsync(new BulkUpdate var results = await contents.BulkUpdateAsync(
{ new BulkUpdate
DoNotScript = false, {
DoNotValidate = false, DoNotScript = false,
Jobs = DoNotValidate = false,
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Type = BulkUpdateType.Upsert,
Data = new
{ {
number = new Type = BulkUpdateType.Upsert,
Data = new
{ {
iv = 99 number = new
{
iv = 99
}
} }
} },
}, ],
], Publish = true
Publish = true });
});
Assert.Single(results); Assert.Single(results);
Assert.Null(results[0].Error); Assert.Null(results[0].Error);
@ -162,26 +168,27 @@ public class ContentScriptingTests(CreatedAppFixture fixture) : IClassFixture<Cr
// STEP 1: Create content with a value that triggers the schema. // STEP 1: Create content with a value that triggers the schema.
var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName); var contents = _.Client.Contents<TestEntity, TestEntityData>(schemaName);
var results = await contents.BulkUpdateAsync(new BulkUpdate var results = await contents.BulkUpdateAsync(
{ new BulkUpdate
DoNotScript = true, {
DoNotValidate = false, DoNotScript = true,
Jobs = DoNotValidate = false,
[ Jobs =
new BulkUpdateJob [
{ new BulkUpdateJob
Type = BulkUpdateType.Upsert,
Data = new
{ {
number = new Type = BulkUpdateType.Upsert,
Data = new
{ {
iv = 99 number = new
{
iv = 99
}
} }
} },
}, ],
], Publish = true
Publish = true });
});
Assert.Single(results); Assert.Single(results);
Assert.Null(results[0].Error); Assert.Null(results[0].Error);

741
tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

File diff suppressed because it is too large

20
tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -92,17 +92,19 @@ public sealed class GraphQLTests(GraphQLFixture fixture) : IClassFixture<GraphQL
public async Task Should_query_json() public async Task Should_query_json()
{ {
// STEP 1: Create a content with JSON. // STEP 1: Create a content with JSON.
var content_0 = await _.Contents.CreateAsync(new TestEntityData var content_0 = await _.Contents.CreateAsync(
{ new TestEntityData
Json = JToken.FromObject(new
{ {
value = 1, Json = JToken.FromObject(new
obj = new
{ {
value = 2 value = 1,
} obj = new
}) {
}, ContentCreateOptions.AsPublish); value = 2
}
})
},
ContentCreateOptions.AsPublish);
// STEP 2: Query this content. // STEP 2: Query this content.

45
tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

@ -100,23 +100,25 @@ public class RuleRunnerTests(ClientFixture fixture, WebhookCatcherFixture webhoo
// Create a test content. // Create a test content.
var referencedContents = app.Contents<TestEntity, TestEntityData>(schemaName); var referencedContents = app.Contents<TestEntity, TestEntityData>(schemaName);
var referencedContent = await referencedContents.CreateAsync(new TestEntityData var referencedContent = await referencedContents.CreateAsync(
{ new TestEntityData
String = contentString {
}); String = contentString
});
var parentSchema = await TestEntityWithReferences.CreateSchemaAsync(app.Schemas, schemaNameRef); var parentSchema = await TestEntityWithReferences.CreateSchemaAsync(app.Schemas, schemaNameRef);
// Create a test content that references the other schema. // Create a test content that references the other schema.
var parentContents = app.Contents<TestEntityWithReferences, TestEntityWithReferencesData>(schemaNameRef); var parentContents = app.Contents<TestEntityWithReferences, TestEntityWithReferencesData>(schemaNameRef);
await parentContents.CreateAsync(new TestEntityWithReferencesData await parentContents.CreateAsync(
{ new TestEntityWithReferencesData
References = {
[ References =
referencedContent.Id [
], referencedContent.Id
}); ],
});
// STEP 2: Create rule. // STEP 2: Create rule.
@ -173,13 +175,15 @@ public class RuleRunnerTests(ClientFixture fixture, WebhookCatcherFixture webhoo
var updatedString = Guid.NewGuid().ToString(); var updatedString = Guid.NewGuid().ToString();
var updateEvent = "ReferenceUpdated"; var updateEvent = "ReferenceUpdated";
await referencedContents.UpdateAsync(referencedContent.Id, new TestEntityData await referencedContents.UpdateAsync(
{ referencedContent.Id,
String = updatedString new TestEntityData
}); {
String = updatedString
});
// Get requests. // STEP 4: Get requests.
var request = await webhookCatcher.PollAsync(sessionId, x => x.IsPost() && x.HasContent(updatedString) && x.HasContent(updateEvent)); var request = await webhookCatcher.PollAsync(sessionId, x => x.IsPost() && x.HasContent(updatedString) && x.HasContent(updateEvent));
AssertRequest(request); AssertRequest(request);
@ -668,10 +672,11 @@ public class RuleRunnerTests(ClientFixture fixture, WebhookCatcherFixture webhoo
// Create a test content. // Create a test content.
var contents = app.Contents<TestEntity, TestEntityData>(schemaName); var contents = app.Contents<TestEntity, TestEntityData>(schemaName);
await contents.CreateAsync(new TestEntityData await contents.CreateAsync(
{ new TestEntityData
String = contentString {
}); String = contentString
});
} }
private static async Task<AssetDto> CreateAssetAsync(ISquidexClient app) private static async Task<AssetDto> CreateAssetAsync(ISquidexClient app)

Loading…
Cancel
Save