Browse Source

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

pull/1235/head
Sebastian Stehle 1 year 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. 22
      tools/TestSuite/TestSuite.ApiTests/AssetTests.cs
  19. 3
      tools/TestSuite/TestSuite.ApiTests/BackupTests.cs
  20. 16
      tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs
  21. 18
      tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs
  22. 31
      tools/TestSuite/TestSuite.ApiTests/ContentLanguageTests.cs
  23. 36
      tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  24. 81
      tools/TestSuite/TestSuite.ApiTests/ContentReferencesTests.cs
  25. 21
      tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs
  26. 339
      tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  27. 6
      tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs
  28. 15
      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);

22
tools/TestSuite/TestSuite.ApiTests/AssetTests.cs

@ -589,7 +589,8 @@ 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"
}); });
@ -600,14 +601,17 @@ 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,
new AnnotateAssetDto
{ {
Metadata = asset_1.Metadata 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'"
}); });
@ -623,7 +627,8 @@ 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()
}); });
@ -648,7 +653,8 @@ 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
}); });
@ -727,7 +733,8 @@ 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"
}); });
@ -774,7 +781,8 @@ 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 Query = new
{ {

3
tools/TestSuite/TestSuite.ApiTests/BackupTests.cs

@ -134,7 +134,8 @@ 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
}); });

16
tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs

@ -30,7 +30,8 @@ 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"
}); });
@ -43,7 +44,9 @@ 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,
new ChangeStatus
{ {
Status = "Published" Status = "Published"
}); });
@ -62,14 +65,16 @@ 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]
}); });
@ -80,7 +85,8 @@ 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"
}); });

18
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" String = "İstanbul"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
await contents.CreateAsync(new SimpleEntityData await contents.CreateAsync(
new SimpleEntityData
{ {
String = "Mersin" String = "Mersin"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
await contents.CreateAsync(new SimpleEntityData await contents.CreateAsync(
new SimpleEntityData
{ {
String = "Lüleburgaz" String = "Lüleburgaz"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Get sorted contents. // STEP 2: Get sorted contents.

31
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?> Localized = new Dictionary<string, string?>
{ {
["de"] = "Hallo", ["de"] = "Hallo",
["en"] = "Hello" ["en"] = "Hello"
} }
}, ContentCreateOptions.AsPublish, QueryContext.Default.WithLanguages("de")); },
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,7 +44,8 @@ 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?> Localized = new Dictionary<string, string?>
{ {
@ -49,7 +53,8 @@ public class ContentLanguageTests(ContentFixture fixture) : IClassFixture<Conten
["en"] = "Hello", ["en"] = "Hello",
["custom"] = "Custom" ["custom"] = "Custom"
} }
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Get content. // STEP 2: Get content.
@ -64,30 +69,38 @@ 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?> Localized = new Dictionary<string, string?>
{ {
["de"] = "Hallo", ["de"] = "Hallo",
["en"] = "Hello" ["en"] = "Hello"
} }
}, ContentCreateOptions.AsPublish); },
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,
new Dictionary<string, string>
{ {
["X-Flatten"] = "1" ["X-Flatten"] = "1"
}); });
var (etag3, _) = await GetEtagAsync(content.Id, new Dictionary<string, string> var (etag3, _) = await GetEtagAsync(
content.Id,
new Dictionary<string, string>
{ {
["X-Languages"] = "en" ["X-Languages"] = "en"
}); });
var (etag4, _) = await GetEtagAsync(content.Id, new Dictionary<string, string> var (etag4, _) = await GetEtagAsync(
content.Id,
new Dictionary<string, string>
{ {
["X-Languages"] = "en", ["X-Languages"] = "en",
["X-Flatten"] = "1" ["X-Flatten"] = "1"

36
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 Json = new JObject
{ {
["search.field.with.dot"] = 42 ["search.field.with.dot"] = 42
} }
}, ContentCreateOptions.AsPublish); },
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.

81
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] References = [contentA_1.Id]
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 3: Query new item. // STEP 3: Query new item.
@ -41,7 +44,9 @@ 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,
new ChangeStatus
{ {
Status = "Published" Status = "Published"
}); });
@ -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 References = null
}, ContentCreateOptions.AsPublish); },
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] References = [contentA_1.Id]
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 3: Try to delete with referrer check. // STEP 3: Try to delete with referrer check.
@ -87,23 +96,29 @@ 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 References = null
}, ContentCreateOptions.AsPublish); },
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] References = [contentA_1.Id]
}, ContentCreateOptions.AsPublish); },
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,
new ChangeStatus
{ {
Status = "Draft", Status = "Draft",
// Ensure that the flag is true. // Ensure that the flag is true.
@ -113,7 +128,9 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
// 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,
new ChangeStatus
{ {
Status = "Draft", Status = "Draft",
// It is the default anyway, just to make it more explicit. // It is the default anyway, just to make it more explicit.
@ -125,21 +142,26 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
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 References = null
}, ContentCreateOptions.AsPublish); },
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] References = [contentA_1.Id]
}, ContentCreateOptions.AsPublish); },
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 =
[ [
@ -157,7 +179,8 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
// 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 =
[ [
@ -178,21 +201,26 @@ 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 References = null
}, ContentCreateOptions.AsPublish); },
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] References = [contentA_1.Id]
}, ContentCreateOptions.AsPublish); },
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 =
[ [
@ -210,7 +238,8 @@ public class ContentReferencesTests(ContentReferencesFixture fixture) : IClassFi
// 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 =
[ [

21
tools/TestSuite/TestSuite.ApiTests/ContentScriptingTests.cs

@ -37,7 +37,8 @@ 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
}); });
@ -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 Number = 13
}, ContentCreateOptions.AsPublish); },
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 Number = 99
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
Assert.Equal(19, content.Data.Number); Assert.Equal(19, content.Data.Number);
} }
@ -114,7 +119,8 @@ 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, DoNotScript = false,
DoNotValidate = false, DoNotValidate = false,
@ -162,7 +168,8 @@ 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, DoNotScript = true,
DoNotValidate = false, DoNotValidate = false,

339
tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

@ -25,14 +25,17 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_return_published_content() public async Task Should_return_published_content()
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}); });
// STEP 2: Publish the item. // STEP 2: Publish the item.
await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
content.Id,
new ChangeStatus
{ {
Status = "Published" Status = "Published"
}); });
@ -46,14 +49,18 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_not_return_archived_content() public async Task Should_not_return_archived_content()
{ {
// STEP 1: Create the item published. // STEP 1: Create the item published.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Archive the item. // STEP 2: Archive the item.
await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
content.Id,
new ChangeStatus
{ {
Status = "Archived" Status = "Archived"
}); });
@ -70,18 +77,24 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_not_return_unpublished_content() public async Task Should_not_return_unpublished_content()
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}); });
// STEP 2: Change the status to publiushed and then to draft. // STEP 2: Change the status to publiushed and then to draft.
await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
content.Id,
new ChangeStatus
{ {
Status = "Published" Status = "Published"
}); });
await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus
await _.Contents.ChangeStatusAsync(
content.Id,
new ChangeStatus
{ {
Status = "Draft" Status = "Draft"
}); });
@ -100,10 +113,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
const string text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"; const string text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36";
// 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.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = text String = text
}, ContentCreateOptions.AsPublish); },
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.
@ -118,13 +133,15 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_null_localized_text() public async Task Should_create_null_localized_text()
{ {
// 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.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Localized = new Dictionary<string, string?> Localized = new Dictionary<string, string?>
{ {
["en"] = null ["en"] = null
} }
}, ContentCreateOptions.AsPublish); },
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.
@ -139,13 +156,15 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_json_with_dot() public async Task Should_create_json_with_dot()
{ {
// 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.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Json = new JObject Json = new JObject
{ {
["field.with.dot"] = 42 ["field.with.dot"] = 42
} }
}, ContentCreateOptions.AsPublish); },
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.
@ -160,10 +179,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_default_text() public async Task Should_create_default_text()
{ {
// 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.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Localized = [] Localized = []
}, ContentCreateOptions.AsPublish); },
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.
@ -176,7 +197,8 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_non_published_content() public async Task Should_create_non_published_content()
{ {
// STEP 1: Create the item unpublished. // STEP 1: Create the item unpublished.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}); });
@ -195,10 +217,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_published_content() public async Task Should_create_published_content()
{ {
// STEP 1: Create the item published. // STEP 1: Create the item published.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Get the item. // STEP 2: Get the item.
@ -211,10 +235,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_create_draft_version() public async Task Should_create_draft_version()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Create draft. // STEP 2: Create draft.
@ -222,7 +248,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Update the item and ensure that the data has not changed. // STEP 3: Update the item and ensure that the data has not changed.
await _.Contents.PatchAsync(content.Id, new TestEntityData await _.Contents.PatchAsync(
content.Id,
new TestEntityData
{ {
Number = 2 Number = 2
}); });
@ -239,7 +267,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 5: Publish draft and ensure that it has been updated. // STEP 5: Publish draft and ensure that it has been updated.
await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus await _.Contents.ChangeStatusAsync(
content.Id,
new ChangeStatus
{ {
Status = "Published" Status = "Published"
}); });
@ -255,12 +285,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
var id = $"custom-{Guid.NewGuid()}"; var id = $"custom-{Guid.NewGuid()}";
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var content = await _.Contents.CreateAsync(
new TestEntityData
var content = await _.Contents.CreateAsync(new TestEntityData
{ {
Number = 1 Number = 1
}, options); },
new ContentCreateOptions { Id = id, Publish = true });
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
} }
@ -271,12 +301,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
var id = $"custom-{Guid.NewGuid()}"; var id = $"custom-{Guid.NewGuid()}";
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var content = await _.Contents.CreateAsync(
new TestEntityData
var content = await _.Contents.CreateAsync(new TestEntityData
{ {
Number = 1 Number = 1
}, options); },
new ContentCreateOptions { Id = id, Publish = true });
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
@ -284,10 +314,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 2: Create a new item with a custom id. // STEP 2: Create a new item with a custom id.
var ex = await Assert.ThrowsAnyAsync<SquidexException>(() => var ex = await Assert.ThrowsAnyAsync<SquidexException>(() =>
{ {
return _.Contents.CreateAsync(new TestEntityData return _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, options); },
new ContentCreateOptions { Id = id, Publish = true });
}); });
Assert.Equal(409, ex.StatusCode); Assert.Equal(409, ex.StatusCode);
@ -299,16 +331,21 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
var id = $"custom-{Guid.NewGuid()}"; var id = $"custom-{Guid.NewGuid()}";
// STEP 1: Upsert a new item with a custom id. // STEP 1: Upsert a new item with a custom id.
var content = await _.Contents.UpsertAsync(id, new TestEntityData var content = await _.Contents.UpsertAsync(
id,
new TestEntityData
{ {
Number = 1 Number = 1
}, ContentUpsertOptions.AsPublish); },
ContentUpsertOptions.AsPublish);
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
// STEP 2: Make an update with the upsert endpoint. // STEP 2: Make an update with the upsert endpoint.
content = await _.Contents.UpsertAsync(id, new TestEntityData content = await _.Contents.UpsertAsync(
id,
new TestEntityData
{ {
Number = 2 Number = 2
}); });
@ -317,7 +354,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Make an update with the update endpoint. // STEP 3: Make an update with the update endpoint.
content = await _.Contents.UpdateAsync(id, new TestEntityData content = await _.Contents.UpdateAsync(
id,
new TestEntityData
{ {
Number = 3 Number = 3
}); });
@ -357,17 +396,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_update_content(ContentStrategies.Update strategy) public async Task Should_update_content(ContentStrategies.Update strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "2" String = "2"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update with selected strategy. // STEP 2: Update with selected strategy.
await _.Client.UpdateAsync(content, new TestEntityData await _.Client.UpdateAsync(
content,
new TestEntityData
{ {
Number = 200 Number = 200
}, strategy); },
strategy);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -381,14 +425,17 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_update_content_with_script() public async Task Should_update_content_with_script()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 100 Number = 100
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update content with script. // STEP 2: Update content with script.
await _.Client.DynamicContents(_.SchemaName).UpdateAsync(content.Id, await _.Client.DynamicContents(_.SchemaName).UpdateAsync(
content.Id,
new DynamicData new DynamicData
{ {
[TestEntityData.NumberField] = new JObject [TestEntityData.NumberField] = new JObject
@ -412,12 +459,14 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_unset_field_with_patch() public async Task Should_unset_field_with_patch()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "Hello", String = "Hello",
// Not relevant for the test, but required in the schema. // Not relevant for the test, but required in the schema.
Number = 100 Number = 100
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update content with script. // STEP 2: Update content with script.
@ -439,12 +488,14 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_unset_field_value_with_patch() public async Task Should_unset_field_value_with_patch()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "Hello", String = "Hello",
// Not relevant for the test, but required in the schema. // Not relevant for the test, but required in the schema.
Number = 100 Number = 100
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update content with script. // STEP 2: Update content with script.
@ -475,17 +526,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_update_content_to_null(ContentStrategies.Update strategy) public async Task Should_update_content_to_null(ContentStrategies.Update strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "initial" String = "initial"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update with selected strategy. // STEP 2: Update with selected strategy.
await _.Client.UpdateAsync(content, new TestEntityData await _.Client.UpdateAsync(
content,
new TestEntityData
{ {
String = null String = null
}, strategy); },
strategy);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -502,17 +558,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_patch_content(ContentStrategies.Patch strategy) public async Task Should_patch_content(ContentStrategies.Patch strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "initial" String = "initial"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Patch with selected strategy. // STEP 2: Patch with selected strategy.
await _.Client.PatchAsync(content, new TestEntityData await _.Client.PatchAsync(
content,
new TestEntityData
{ {
Number = 200 Number = 200
}, strategy); },
strategy);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -526,10 +587,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_patch_content_with_script() public async Task Should_patch_content_with_script()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 100 Number = 100
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Patch content with script. // STEP 2: Patch content with script.
@ -563,17 +626,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_patch_id_data_value(ContentStrategies.Patch strategy) public async Task Should_patch_id_data_value(ContentStrategies.Patch strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Id = "id1" Id = "id1"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Patch with selected strategy. // STEP 2: Patch with selected strategy.
await _.Client.PatchAsync(content, new TestEntityData await _.Client.PatchAsync(
content,
new TestEntityData
{ {
Id = "id2" Id = "id2"
}, strategy); },
strategy);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -590,17 +658,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_patch_content_to_null(ContentStrategies.Patch strategy) public async Task Should_patch_content_to_null(ContentStrategies.Patch strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
String = "initial" String = "initial"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Patch with selected strategy. // STEP 2: Patch with selected strategy.
await _.Client.PatchAsync(content, new await _.Client.PatchAsync(
content,
new
{ {
@string = new { iv = (string?)null } @string = new { iv = (string?)null }
}, strategy); },
strategy);
var updated = await _.Contents.GetAsync(content.Id); var updated = await _.Contents.GetAsync(content.Id);
@ -615,10 +688,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_delete_content(ContentStrategies.Deletion strategy) public async Task Should_delete_content(ContentStrategies.Deletion strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Delete with selected strategy. // STEP 2: Delete with selected strategy.
@ -649,12 +724,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
var id = $"custom-{Guid.NewGuid()}"; var id = $"custom-{Guid.NewGuid()}";
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var content = await _.Contents.CreateAsync(
new TestEntityData
var content = await _.Contents.CreateAsync(new TestEntityData
{ {
Number = 1 Number = 1
}, options); },
new ContentCreateOptions { Id = id, Publish = true });
Assert.Equal(id, content.Id); Assert.Equal(id, content.Id);
@ -677,10 +752,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_recreate_deleted_content(ContentStrategies.Deletion strategy) public async Task Should_recreate_deleted_content(ContentStrategies.Deletion strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content_1 = await _.Contents.CreateAsync(new TestEntityData var content_1 = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Delete with selected strategy. // STEP 2: Delete with selected strategy.
@ -688,12 +765,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Recreate the item with the same id. // STEP 3: Recreate the item with the same id.
var createOptions = new ContentCreateOptions { Id = content_1.Id, Publish = true }; var content_2 = await _.Contents.CreateAsync(
new TestEntityData
var content_2 = await _.Contents.CreateAsync(new TestEntityData
{ {
Number = 2 Number = 2
}, createOptions); },
new ContentCreateOptions { Id = content_1.Id, Publish = true });
Assert.Equal(Status.Published, content_2.Status); Assert.Equal(Status.Published, content_2.Status);
@ -714,10 +791,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_recreate_deleted_content_with_upsert(ContentStrategies.Deletion strategy) public async Task Should_recreate_deleted_content_with_upsert(ContentStrategies.Deletion strategy)
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content_1 = await _.Contents.CreateAsync(new TestEntityData var content_1 = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Delete with selected strategy. // STEP 2: Delete with selected strategy.
@ -725,10 +804,13 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Recreate the item with the same id. // STEP 3: Recreate the item with the same id.
var content_2 = await _.Contents.UpsertAsync(content_1.Id, new TestEntityData var content_2 = await _.Contents.UpsertAsync(
content_1.Id,
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentUpsertOptions.AsPublish); },
ContentUpsertOptions.AsPublish);
Assert.Equal(Status.Published, content_2.Status); Assert.Equal(Status.Published, content_2.Status);
} }
@ -745,10 +827,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 1: Create a new item with a custom id. // STEP 1: Create a new item with a custom id.
var options = new ContentCreateOptions { Id = id, Publish = true }; var options = new ContentCreateOptions { Id = id, Publish = true };
var content_1 = await _.Contents.CreateAsync(new TestEntityData var content_1 = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, options); },
options);
// STEP 2: Permanently delete content with custom id. // STEP 2: Permanently delete content with custom id.
@ -756,10 +840,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Create a new item with same custom id. // STEP 3: Create a new item with same custom id.
var content_2 = await _.Contents.CreateAsync(new TestEntityData var content_2 = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, options); },
options);
Assert.Equal(2, content_2.Data.Number); Assert.Equal(2, content_2.Data.Number);
@ -801,7 +887,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Update content. // STEP 3: Update content.
var content_2 = await client.UpdateAsync("_schemaId_", new DynamicData var content_2 = await client.UpdateAsync(
"_schemaId_",
new DynamicData
{ {
["my-field"] = new JObject ["my-field"] = new JObject
{ {
@ -818,14 +906,18 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_get_content_by_version() public async Task Should_get_content_by_version()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 1 Number = 1
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update content. // STEP 2: Update content.
content = await _.Contents.UpdateAsync(content.Id, new TestEntityData content = await _.Contents.UpdateAsync(
content.Id,
new TestEntityData
{ {
Number = 2 Number = 2
}); });
@ -855,10 +947,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_update_content_in_parallel() public async Task Should_update_content_in_parallel()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 3: Make parallel updates. // STEP 3: Make parallel updates.
@ -866,10 +960,13 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
{ {
try try
{ {
await _.Contents.UpdateAsync(content.Id, new TestEntityData await _.Contents.UpdateAsync(
content.Id,
new TestEntityData
{ {
Number = i Number = i
}, ct: ct); },
ct: ct);
} }
catch (SquidexException ex) when (ex.StatusCode is 409 or 412) catch (SquidexException ex) when (ex.StatusCode is 409 or 412)
{ {
@ -879,7 +976,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Make an normal update to ensure nothing is corrupt. // STEP 3: Make an normal update to ensure nothing is corrupt.
await _.Contents.UpdateAsync(content.Id, new TestEntityData await _.Contents.UpdateAsync(
content.Id,
new TestEntityData
{ {
Number = 2 Number = 2
}); });
@ -893,10 +992,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_upsert_content_in_parallel() public async Task Should_upsert_content_in_parallel()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Number = 2 Number = 2
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 3: Make parallel upserts. // STEP 3: Make parallel upserts.
@ -904,10 +1005,13 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
{ {
try try
{ {
await _.Contents.UpsertAsync(content.Id, new TestEntityData await _.Contents.UpsertAsync(
content.Id,
new TestEntityData
{ {
Number = i Number = i
}, ct: ct); },
ct: ct);
} }
catch (SquidexException ex) when (ex.StatusCode is 409 or 412) catch (SquidexException ex) when (ex.StatusCode is 409 or 412)
{ {
@ -917,7 +1021,9 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 3: Make an normal update to ensure nothing is corrupt. // STEP 3: Make an normal update to ensure nothing is corrupt.
await _.Contents.UpdateAsync(content.Id, new TestEntityData await _.Contents.UpdateAsync(
content.Id,
new TestEntityData
{ {
Number = 2 Number = 2
}); });
@ -1118,7 +1224,8 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
var prefix = Guid.NewGuid().ToString(); var prefix = Guid.NewGuid().ToString();
// STEP 1: Create contents. // STEP 1: Create contents.
var result_0 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result_0 = await _.Contents.BulkUpdateAsync(
new BulkUpdate
{ {
Jobs = Jobs =
[ [
@ -1158,7 +1265,8 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 2: Update contents by filter. // STEP 2: Update contents by filter.
var result_1 = await _.Contents.BulkUpdateAsync(new BulkUpdate var result_1 = await _.Contents.BulkUpdateAsync(
new BulkUpdate
{ {
Jobs = Jobs =
[ [
@ -1221,7 +1329,8 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
// STEP 2: Get contents. // STEP 2: Get contents.
var contents = await _.Contents.GetAsync(new ContentQuery var contents = await _.Contents.GetAsync(
new ContentQuery
{ {
Ids = result_0.Select(x => x.ContentId).ToHashSet() Ids = result_0.Select(x => x.ContentId).ToHashSet()
}); });
@ -1237,10 +1346,12 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
public async Task Should_prevent_update() public async Task Should_prevent_update()
{ {
// STEP 1: Create a new item. // STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(new TestEntityData var content = await _.Contents.CreateAsync(
new TestEntityData
{ {
Immutable = "v1" Immutable = "v1"
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Update content, but do not change immutable. // STEP 2: Update content, but do not change immutable.
@ -1257,4 +1368,22 @@ public class ContentUpdateTests(ContentFixture fixture) : IClassFixture<ContentF
Assert.Equal(400, ex.StatusCode); Assert.Equal(400, ex.StatusCode);
Assert.Contains("Validation error: immutable.iv: Field cannot be changed", ex.ToString()); Assert.Contains("Validation error: immutable.iv: Field cannot be changed", ex.ToString());
} }
[Fact]
public async Task Should_create_content_and_only_return_subset_of_fields()
{
// STEP 1: Create a new item.
var content = await _.Contents.CreateAsync(
new TestEntityData
{
String = "Hello",
// No included in results.
Number = 42,
},
ContentCreateOptions.AsPublish,
QueryContext.Default.WithFields(TestEntityData.StringField));
Assert.Equal(0, content.Data.Number);
}
} }

6
tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -92,7 +92,8 @@ 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 Json = JToken.FromObject(new
{ {
@ -102,7 +103,8 @@ public sealed class GraphQLTests(GraphQLFixture fixture) : IClassFixture<GraphQL
value = 2 value = 2
} }
}) })
}, ContentCreateOptions.AsPublish); },
ContentCreateOptions.AsPublish);
// STEP 2: Query this content. // STEP 2: Query this content.

15
tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

@ -100,7 +100,8 @@ 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
}); });
@ -110,7 +111,8 @@ public class RuleRunnerTests(ClientFixture fixture, WebhookCatcherFixture webhoo
// 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 =
[ [
@ -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,
new TestEntityData
{ {
String = updatedString 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,7 +672,8 @@ 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
}); });

Loading…
Cancel
Save