Browse Source

User in graphql. (#682)

* User in graphql.

* Formatting.
pull/687/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
b3e136943c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  2. 16
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs
  3. 16
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs
  4. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs
  5. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs
  6. 58
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs
  7. 2
      backend/src/Squidex.Shared/Users/ClientUser.cs
  8. 5
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  9. 13
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs
  10. 11
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  11. 46
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/UserMocks.cs
  12. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs
  13. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppContributorsTests.cs
  14. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InvitationEventConsumerTests.cs
  15. 22
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs
  16. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs
  17. 30
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs
  18. 25
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/CommentTriggerHandlerTests.cs
  19. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs
  20. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  21. 14
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs
  22. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs
  23. 28
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs
  24. 68
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Notifications/NotificationEmailSenderTests.cs
  25. 30
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/Mocks.cs

32
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -16,6 +16,7 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Log; using Squidex.Log;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
@ -24,6 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>(); private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>();
private static readonly List<IEnrichedContentEntity> EmptyContents = new List<IEnrichedContentEntity>(); private static readonly List<IEnrichedContentEntity> EmptyContents = new List<IEnrichedContentEntity>();
private readonly IDataLoaderContextAccessor dataLoaders; private readonly IDataLoaderContextAccessor dataLoaders;
private readonly IUserResolver userResolver;
public IUrlGenerator UrlGenerator { get; } public IUrlGenerator UrlGenerator { get; }
@ -35,10 +37,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public GraphQLExecutionContext(IAssetQueryService assetQuery, IContentQueryService contentQuery, public GraphQLExecutionContext(IAssetQueryService assetQuery, IContentQueryService contentQuery,
Context context, Context context,
IDataLoaderContextAccessor dataLoaders, ICommandBus commandBus, IUrlGenerator urlGenerator, ISemanticLog log) IDataLoaderContextAccessor dataLoaders,
ICommandBus commandBus, IUrlGenerator urlGenerator, IUserResolver userResolver,
ISemanticLog log)
: base(assetQuery, contentQuery) : base(assetQuery, contentQuery)
{ {
this.dataLoaders = dataLoaders; this.dataLoaders = dataLoaders;
this.userResolver = userResolver;
CommandBus = commandBus; CommandBus = commandBus;
@ -51,6 +56,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Log = log; Log = log;
} }
public async Task<IUser> FindUserAsync(RefToken refToken)
{
if (refToken.IsClient)
{
return new ClientUser(refToken);
}
else
{
var dataLoader = GetUserLoader();
return await dataLoader.LoadAsync(refToken.Identifier).GetResultAsync();
}
}
public async Task<IEnrichedAssetEntity?> FindAssetAsync(DomainId id) public async Task<IEnrichedAssetEntity?> FindAssetAsync(DomainId id)
{ {
var dataLoader = GetAssetsLoader(); var dataLoader = GetAssetsLoader();
@ -115,6 +134,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}); });
} }
private IDataLoader<string, IUser> GetUserLoader()
{
return dataLoaders.Context.GetOrAddBatchLoader<string, IUser>(nameof(GetUserLoader),
async batch =>
{
var result = await userResolver.QueryManyAsync(batch.ToArray());
return result;
});
}
private static async Task<IReadOnlyList<T>> LoadManyAsync<TKey, T>(IDataLoader<TKey, T> dataLoader, ICollection<TKey> keys) where T : class private static async Task<IReadOnlyList<T>> LoadManyAsync<TKey, T>(IDataLoader<TKey, T> dataLoader, ICollection<TKey> keys) where T : class
{ {
var contents = await Task.WhenAll(keys.Select(x => dataLoader.LoadAsync(x).GetResultAsync())); var contents = await Task.WhenAll(keys.Select(x => dataLoader.LoadAsync(x).GetResultAsync()));

16
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs

@ -54,6 +54,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets
Description = "The user that has created the asset." Description = "The user that has created the asset."
}); });
AddField(new FieldType
{
Name = "createdByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.CreatedByUser,
Description = "The full info of the user that has created the asset."
});
AddField(new FieldType AddField(new FieldType
{ {
Name = "lastModified", Name = "lastModified",
@ -70,6 +78,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets
Description = "The user that has updated the asset last." Description = "The user that has updated the asset last."
}); });
AddField(new FieldType
{
Name = "lastModifiedByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.LastModifiedByUser,
Description = "The full info of the user that has created the asset."
});
AddField(new FieldType AddField(new FieldType
{ {
Name = "mimeType", Name = "mimeType",

16
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs

@ -46,6 +46,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Description = "The user that has created the content." Description = "The user that has created the content."
}; };
public static readonly FieldType CreatedByUser = new FieldType
{
Name = "createdByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.CreatedByUser,
Description = "The full info of the user that has created the content."
};
public static readonly FieldType LastModified = new FieldType public static readonly FieldType LastModified = new FieldType
{ {
Name = "lastModified", Name = "lastModified",
@ -62,6 +70,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Description = "The user that has updated the content last." Description = "The user that has updated the content last."
}; };
public static readonly FieldType LastModifiedByUser = new FieldType
{
Name = "lastModifiedByUser",
ResolvedType = UserGraphType.NonNull,
Resolver = EntityResolvers.LastModifiedByUser,
Description = "The full info of the user that has updated the content last."
};
public static readonly FieldType Status = new FieldType public static readonly FieldType Status = new FieldType
{ {
Name = "status", Name = "status",

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs

@ -27,8 +27,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
AddField(ContentFields.Version); AddField(ContentFields.Version);
AddField(ContentFields.Created); AddField(ContentFields.Created);
AddField(ContentFields.CreatedBy); AddField(ContentFields.CreatedBy);
AddField(ContentFields.CreatedByUser);
AddField(ContentFields.LastModified); AddField(ContentFields.LastModified);
AddField(ContentFields.LastModifiedBy); AddField(ContentFields.LastModifiedBy);
AddField(ContentFields.LastModifiedByUser);
AddField(ContentFields.Status); AddField(ContentFields.Status);
AddField(ContentFields.StatusColor); AddField(ContentFields.StatusColor);

9
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs

@ -7,6 +7,8 @@
using System; using System;
using GraphQL.Resolvers; using GraphQL.Resolvers;
using Squidex.Infrastructure;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
{ {
@ -15,13 +17,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id.ToString()); public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id.ToString());
public static readonly IFieldResolver Created = Resolve<IEntity>(x => x.Created.ToDateTimeUtc()); public static readonly IFieldResolver Created = Resolve<IEntity>(x => x.Created.ToDateTimeUtc());
public static readonly IFieldResolver CreatedBy = Resolve<IEntityWithCreatedBy>(x => x.CreatedBy.ToString()); public static readonly IFieldResolver CreatedBy = Resolve<IEntityWithCreatedBy>(x => x.CreatedBy.ToString());
public static readonly IFieldResolver CreatedByUser = ResolveUser<IEntityWithCreatedBy>(x => x.CreatedBy);
public static readonly IFieldResolver LastModified = Resolve<IEntity>(x => x.LastModified.ToDateTimeUtc()); public static readonly IFieldResolver LastModified = Resolve<IEntity>(x => x.LastModified.ToDateTimeUtc());
public static readonly IFieldResolver LastModifiedBy = Resolve<IEntityWithLastModifiedBy>(x => x.LastModifiedBy.ToString()); public static readonly IFieldResolver LastModifiedBy = Resolve<IEntityWithLastModifiedBy>(x => x.LastModifiedBy.ToString());
public static readonly IFieldResolver LastModifiedByUser = ResolveUser<IEntityWithLastModifiedBy>(x => x.LastModifiedBy);
public static readonly IFieldResolver Version = Resolve<IEntityWithVersion>(x => x.Version); public static readonly IFieldResolver Version = Resolve<IEntityWithVersion>(x => x.Version);
private static IFieldResolver Resolve<TSource>(Func<TSource, object> resolver) private static IFieldResolver Resolve<TSource>(Func<TSource, object> resolver)
{ {
return Resolvers.Sync(resolver); return Resolvers.Sync(resolver);
} }
private static IFieldResolver ResolveUser<TSource>(Func<TSource, RefToken> resolver)
{
return Resolvers.Async<TSource, IUser>((source, _, context) => context.FindUserAsync(resolver(source)));
}
} }
} }

58
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs

@ -0,0 +1,58 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Shared.Identity;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
internal sealed class UserGraphType : ObjectGraphType<IUser>
{
public static readonly IGraphType Nullable = new UserGraphType();
public static readonly IGraphType NonNull = new NonNullGraphType(Nullable);
public UserGraphType()
{
Name = "User";
AddField(new FieldType
{
Name = "id",
Resolver = Resolve(x => x.Id),
ResolvedType = AllTypes.NonNullString,
Description = "The id of the user."
});
AddField(new FieldType
{
Name = "displayName",
Resolver = Resolve(x => x.Claims.DisplayName()),
ResolvedType = AllTypes.String,
Description = "The display name of the user."
});
AddField(new FieldType
{
Name = "email",
Resolver = Resolve(x => x.Email),
ResolvedType = AllTypes.String,
Description = "The email of the user."
});
Description = "A user that created or modified a content or asset.";
}
private static IFieldResolver Resolve<T>(Func<IUser, T> resolver)
{
return Resolvers.Sync(resolver);
}
}
}

2
backend/src/Squidex.Shared/Users/ClientUser.cs

@ -49,7 +49,7 @@ namespace Squidex.Shared.Users
claims = new List<Claim> claims = new List<Claim>
{ {
new Claim(OpenIdClaims.ClientId, token.Identifier), new Claim(OpenIdClaims.ClientId, token.Identifier),
new Claim(SquidexClaimTypes.DisplayName, token.ToString()) new Claim(SquidexClaimTypes.DisplayName, token.Identifier)
}; };
} }
} }

5
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -106,7 +106,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
if (resources.CanCreateContent(Name)) if (resources.CanCreateContent(Name))
{ {
AddPostLink("contents/create", resources.Url<ContentsController>(x => nameof(x.PostContent), values)); AddPostLink("contents/create", resources.Url<ContentsController>(x => nameof(x.PostContent), values));
AddPostLink("contents/create/publish", resources.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true"); AddPostLink("contents/create/publish", resources.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true");
} }
@ -130,10 +129,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
AddPutLink("fields/order", resources.Url<SchemaFieldsController>(x => nameof(x.PutSchemaFieldOrdering), values)); AddPutLink("fields/order", resources.Url<SchemaFieldsController>(x => nameof(x.PutSchemaFieldOrdering), values));
AddPutLink("update", resources.Url<SchemasController>(x => nameof(x.PutSchema), values)); AddPutLink("update", resources.Url<SchemasController>(x => nameof(x.PutSchema), values));
AddPutLink("update/category", resources.Url<SchemasController>(x => nameof(x.PutCategory), values));
AddPutLink("update/rules", resources.Url<SchemasController>(x => nameof(x.PutRules), values));
AddPutLink("update/sync", resources.Url<SchemasController>(x => nameof(x.PutSchemaSync), values)); AddPutLink("update/sync", resources.Url<SchemasController>(x => nameof(x.PutSchemaSync), values));
AddPutLink("update/urls", resources.Url<SchemasController>(x => nameof(x.PutPreviewUrls), values)); AddPutLink("update/urls", resources.Url<SchemasController>(x => nameof(x.PutPreviewUrls), values));
AddPutLink("update/rules", resources.Url<SchemasController>(x => nameof(x.PutRules), values));
AddPutLink("update/category", resources.Url<SchemasController>(x => nameof(x.PutCategory), values));
} }
if (resources.CanUpdateSchemaScripts(Name)) if (resources.CanUpdateSchemaScripts(Name))

13
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
{ {
public class RuleEventFormatterCompareTests public class RuleEventFormatterCompareTests
{ {
private readonly IUser user = A.Fake<IUser>(); private readonly IUser user = UserMocks.User("user123", "me@email.com", "me");
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>(); private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema");
@ -74,15 +74,6 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => urlGenerator.AssetContent(appId, "file-name")) A.CallTo(() => urlGenerator.AssetContent(appId, "file-name"))
.Returns("asset-content-slug-url"); .Returns("asset-content-slug-url");
A.CallTo(() => user.Id)
.Returns("user123");
A.CallTo(() => user.Email)
.Returns("me@email.com");
A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
var formatters = new IRuleEventFormatter[] var formatters = new IRuleEventFormatter[]
{ {
new PredefinedPatternsFormatter(urlGenerator), new PredefinedPatternsFormatter(urlGenerator),
@ -247,7 +238,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var result = await sut.FormatAsync(script, @event); var result = await sut.FormatAsync(script, @event);
Assert.Equal("From client:android (client:android, android)", result); Assert.Equal("From android (client:android, android)", result);
} }
[Theory] [Theory]

11
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs

@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
{ {
public class RuleEventFormatterTests public class RuleEventFormatterTests
{ {
private readonly IUser user = A.Fake<IUser>(); private readonly IUser user = UserMocks.User("user123", "me@email.com", "me");
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>(); private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema");
@ -68,15 +68,6 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => urlGenerator.AssetContent(appId, assetId.ToString())) A.CallTo(() => urlGenerator.AssetContent(appId, assetId.ToString()))
.Returns("asset-content-url"); .Returns("asset-content-url");
A.CallTo(() => user.Id)
.Returns("user123");
A.CallTo(() => user.Email)
.Returns("me@email.com");
A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
var formatters = new IRuleEventFormatter[] var formatters = new IRuleEventFormatter[]
{ {
new PredefinedPatternsFormatter(urlGenerator), new PredefinedPatternsFormatter(urlGenerator),

46
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/UserMocks.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Security.Claims;
using FakeItEasy;
using Squidex.Shared.Identity;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Core.TestHelpers
{
public static class UserMocks
{
public static IUser User(string id, string? email = null, string? name = null, bool consent = false)
{
var claims = new List<Claim>();
if (!string.IsNullOrWhiteSpace(name))
{
claims.Add(new Claim(SquidexClaimTypes.DisplayName, name));
}
if (consent)
{
claims.Add(new Claim(SquidexClaimTypes.Consent, "True"));
}
var user = A.Fake<IUser>();
A.CallTo(() => user.Id)
.Returns(id);
A.CallTo(() => user.Email)
.Returns(email ?? id);
A.CallTo(() => user.Claims)
.Returns(claims);
return user;
}
}
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Plans; using Squidex.Domain.Apps.Entities.Apps.Plans;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
{ {
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>(); private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>();
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>(); private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>();
private readonly IUser user = A.Fake<IUser>(); private readonly IUser user;
private readonly IUserResolver userResolver = A.Fake<IUserResolver>(); private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly string contributorId = DomainId.NewGuid().ToString(); private readonly string contributorId = DomainId.NewGuid().ToString();
private readonly string clientId = "client"; private readonly string clientId = "client";
@ -48,8 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
public AppDomainObjectTests() public AppDomainObjectTests()
{ {
A.CallTo(() => user.Id) user = UserMocks.User(contributorId);
.Returns(contributorId);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(contributorId)) A.CallTo(() => userResolver.FindByIdOrEmailAsync(contributorId))
.Returns(user); .Returns(user);

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppContributorsTests.cs

@ -23,9 +23,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards
{ {
public class GuardAppContributorsTests : IClassFixture<TranslationsFixture> public class GuardAppContributorsTests : IClassFixture<TranslationsFixture>
{ {
private readonly IUser user1 = A.Fake<IUser>(); private readonly IUser user1 = UserMocks.User("1");
private readonly IUser user2 = A.Fake<IUser>(); private readonly IUser user2 = UserMocks.User("2");
private readonly IUser user3 = A.Fake<IUser>(); private readonly IUser user3 = UserMocks.User("3");
private readonly IUserResolver users = A.Fake<IUserResolver>(); private readonly IUserResolver users = A.Fake<IUserResolver>();
private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>(); private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>();
private readonly AppContributors contributors_0 = AppContributors.Empty; private readonly AppContributors contributors_0 = AppContributors.Empty;

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InvitationEventConsumerTests.cs

@ -9,6 +9,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Notifications; using Squidex.Domain.Apps.Entities.Notifications;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -23,8 +24,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
{ {
private readonly INotificationSender notificatíonSender = A.Fake<INotificationSender>(); private readonly INotificationSender notificatíonSender = A.Fake<INotificationSender>();
private readonly IUserResolver userResolver = A.Fake<IUserResolver>(); private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly IUser assigner = A.Fake<IUser>(); private readonly IUser assigner = UserMocks.User("1");
private readonly IUser assignee = A.Fake<IUser>(); private readonly IUser assignee = UserMocks.User("2");
private readonly ISemanticLog log = A.Fake<ISemanticLog>(); private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly string assignerId = DomainId.NewGuid().ToString(); private readonly string assignerId = DomainId.NewGuid().ToString();
private readonly string assigneeId = DomainId.NewGuid().ToString(); private readonly string assigneeId = DomainId.NewGuid().ToString();

22
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -37,9 +38,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
new CommandContext(command, commandBus) new CommandContext(command, commandBus)
.Complete(app); .Complete(app);
var user = CreateUser("123"); var user = UserMocks.User("123", command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true)) A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.Returns((user, true)); .Returns((user, true));
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
Assert.Same(context.Result<InvitedResult>().App, app); Assert.Same(context.Result<InvitedResult>().App, app);
Assert.Equal(user.Id, command.ContributorId); Assert.Equal(user.Id, command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true)) A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -60,9 +61,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
new CommandContext(command, commandBus) new CommandContext(command, commandBus)
.Complete(app); .Complete(app);
var user = CreateUser("123"); var user = UserMocks.User("123", command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true)) A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.Returns((user, false)); .Returns((user, false));
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -70,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
Assert.Same(context.Result<IAppEntity>(), app); Assert.Same(context.Result<IAppEntity>(), app);
Assert.Equal(user.Id, command.ContributorId); Assert.Equal(user.Id, command.ContributorId);
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync("me@email.com", true)) A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(user.Email, true))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -103,14 +104,5 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(A<string>._, A<bool>._)) A.CallTo(() => userResolver.CreateUserIfNotExistsAsync(A<string>._, A<bool>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
private static IUser CreateUser(string id)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
return user;
}
} }
} }

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Notifications; using Squidex.Domain.Apps.Entities.Notifications;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
using Squidex.Shared.Users; using Squidex.Shared.Users;
@ -157,10 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans
{ {
if (email != null) if (email != null)
{ {
var user = A.Fake<IUser>(); var user = UserMocks.User(id, email);
A.CallTo(() => user.Email)
.Returns(email);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(id)) A.CallTo(() => userResolver.FindByIdOrEmailAsync(id))
.Returns(user); .Returns(user);

30
backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs

@ -29,13 +29,13 @@ namespace Squidex.Domain.Apps.Entities.Backup
[Fact] [Fact]
public async Task Should_backup_users_but_no_clients() public async Task Should_backup_users_but_no_clients()
{ {
sut.Backup("user1"); sut.Backup("1");
sut.Backup(Subject("user2")); sut.Backup(Subject("2"));
sut.Backup(Client("client")); sut.Backup(Client("client"));
var user1 = CreateUser("user1", "mail1@squidex.io"); var user1 = UserMocks.User("1", "1@email.com");
var user2 = CreateUser("user2", "mail2@squidex.io"); var user2 = UserMocks.User("2", "1@email.com");
var users = new Dictionary<string, IUser> var users = new Dictionary<string, IUser>
{ {
@ -67,8 +67,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
[Fact] [Fact]
public async Task Should_restore_users() public async Task Should_restore_users()
{ {
var user1 = CreateUser("user1", "mail1@squidex.io"); var user1 = UserMocks.User("1", "1@email.com");
var user2 = CreateUser("user2", "mail2@squidex.io"); var user2 = UserMocks.User("2", "2@email.com");
var reader = SetupReader(user1, user2); var reader = SetupReader(user1, user2);
@ -82,11 +82,11 @@ namespace Squidex.Domain.Apps.Entities.Backup
await sut.RestoreAsync(reader, userResolver); await sut.RestoreAsync(reader, userResolver);
Assert.True(sut.TryMap("user1_old", out var mapped1)); Assert.True(sut.TryMap("1_old", out var mapped1));
Assert.True(sut.TryMap(Subject("user2_old"), out var mapped2)); Assert.True(sut.TryMap(Subject("2_old"), out var mapped2));
Assert.Equal(Subject("user1"), mapped1); Assert.Equal(Subject("1"), mapped1);
Assert.Equal(Subject("user2"), mapped2); Assert.Equal(Subject("2"), mapped2);
} }
[Fact] [Fact]
@ -107,16 +107,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
Assert.Same(client, mapped); Assert.Same(client, mapped);
} }
private static IUser CreateUser(string id, string email)
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
return user;
}
private static IBackupReader SetupReader(params IUser[] users) private static IBackupReader SetupReader(params IUser[] users)
{ {
var storedUsers = users.ToDictionary(x => $"{x.Id}_old", x => x.Email); var storedUsers = users.ToDictionary(x => $"{x.Id}_old", x => x.Email);

25
backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/CommentTriggerHandlerTests.cs

@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Comments; using Squidex.Domain.Apps.Events.Comments;
using Squidex.Domain.Apps.Events.Contents; using Squidex.Domain.Apps.Events.Contents;
@ -52,8 +53,8 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact] [Fact]
public async Task Should_create_enriched_events() public async Task Should_create_enriched_events()
{ {
var user1 = CreateUser("1"); var user1 = UserMocks.User("1");
var user2 = CreateUser("2"); var user2 = UserMocks.User("2");
var users = new List<IUser> { user1, user2 }; var users = new List<IUser> { user1, user2 };
var userIds = users.Select(x => x.Id).ToArray(); var userIds = users.Select(x => x.Id).ToArray();
@ -79,8 +80,8 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact] [Fact]
public async Task Should_not_create_enriched_events_when_users_cannot_be_resolved() public async Task Should_not_create_enriched_events_when_users_cannot_be_resolved()
{ {
var user1 = CreateUser("1"); var user1 = UserMocks.User("1");
var user2 = CreateUser("2"); var user2 = UserMocks.User("2");
var users = new List<IUser> { user1, user2 }; var users = new List<IUser> { user1, user2 };
var userIds = users.Select(x => x.Id).ToArray(); var userIds = users.Select(x => x.Id).ToArray();
@ -213,9 +214,9 @@ namespace Squidex.Domain.Apps.Entities.Comments
[Fact] [Fact]
public void Should_trigger_check_when_email_is_correct() public void Should_trigger_check_when_email_is_correct()
{ {
TestForRealCondition("event.mentionedUser.email == 'sebastian@squidex.io'", (handler, trigger) => TestForRealCondition("event.mentionedUser.email == '1@email.com'", (handler, trigger) =>
{ {
var user = CreateUser("1"); var user = UserMocks.User("1", "1@email.com");
var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger); var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger);
@ -228,7 +229,7 @@ namespace Squidex.Domain.Apps.Entities.Comments
{ {
TestForRealCondition("event.mentionedUser.email == 'other@squidex.io'", (handler, trigger) => TestForRealCondition("event.mentionedUser.email == 'other@squidex.io'", (handler, trigger) =>
{ {
var user = CreateUser("1"); var user = UserMocks.User("1");
var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger); var result = handler.Trigger(new EnrichedCommentEvent { MentionedUser = user }, trigger);
@ -262,16 +263,6 @@ namespace Squidex.Domain.Apps.Entities.Comments
}); });
} }
private static IUser CreateUser(string id, string email = "sebastian@squidex.io")
{
var user = A.Fake<IUser>();
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
return user;
}
private void TestForRealCondition(string condition, Action<IRuleTriggerHandler, CommentTrigger> action) private void TestForRealCondition(string condition, Action<IRuleTriggerHandler, CommentTrigger> action)
{ {
var trigger = new CommentTrigger var trigger = new CommentTrigger

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Orleans; using Orleans;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Domain.Apps.Entities.Comments.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
@ -138,10 +139,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject
private void SetupUser(string id, string email) private void SetupUser(string id, string email)
{ {
var user = A.Fake<IUser>(); var user = UserMocks.User(id, email);
A.CallTo(() => user.Id).Returns(id);
A.CallTo(() => user.Email).Returns(email);
A.CallTo(() => userResolver.FindByIdOrEmailAsync(email)) A.CallTo(() => userResolver.FindByIdOrEmailAsync(email))
.Returns(user); .Returns(user);

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -229,7 +229,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
created = content.Created, created = content.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = content.LastModified, lastModified = content.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "client:client1",
status = "DRAFT", status = "DRAFT",
statusColor = "red", statusColor = "red",
url = $"contents/my-schema/{content.Id}", url = $"contents/my-schema/{content.Id}",

14
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using GraphQL; using GraphQL;
@ -32,6 +33,7 @@ using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Log; using Squidex.Log;
using Squidex.Shared; using Squidex.Shared;
using Squidex.Shared.Users;
using Xunit; using Xunit;
#pragma warning disable SA1401 // Fields must be private #pragma warning disable SA1401 // Fields must be private
@ -46,6 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>(); protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>();
protected readonly ICommandBus commandBus = A.Fake<ICommandBus>(); protected readonly ICommandBus commandBus = A.Fake<ICommandBus>();
protected readonly IContentQueryService contentQuery = A.Fake<IContentQueryService>(); protected readonly IContentQueryService contentQuery = A.Fake<IContentQueryService>();
protected readonly IUserResolver userResolver = A.Fake<IUserResolver>();
protected readonly ISchemaEntity schema; protected readonly ISchemaEntity schema;
protected readonly ISchemaEntity schemaRef1; protected readonly ISchemaEntity schemaRef1;
protected readonly ISchemaEntity schemaRef2; protected readonly ISchemaEntity schemaRef2;
@ -63,6 +66,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
app = Mocks.App(appId, Language.DE, Language.GermanGermany); app = Mocks.App(appId, Language.DE, Language.GermanGermany);
A.CallTo(() => userResolver.QueryManyAsync(A<string[]>._))
.ReturnsLazily(x =>
{
var ids = x.GetArgument<string[]>(0)!;
var users = ids.Select(id => UserMocks.User(id, $"{id}@email.com", $"name_{id}"));
return Task.FromResult(users.ToDictionary(x => x.Id));
});
var schemaDef = var schemaDef =
new Schema(schemaId.Name) new Schema(schemaId.Name)
.Publish() .Publish()
@ -201,6 +214,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
.AddSingleton(contentQuery) .AddSingleton(contentQuery)
.AddSingleton(dataLoaderContext) .AddSingleton(dataLoaderContext)
.AddSingleton(dataLoaderListener) .AddSingleton(dataLoaderListener)
.AddSingleton(userResolver)
.AddSingleton<InstantGraphType>() .AddSingleton<InstantGraphType>()
.AddSingleton<JsonGraphType>() .AddSingleton<JsonGraphType>()
.AddSingleton<JsonNoopGraphType>() .AddSingleton<JsonNoopGraphType>()

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs

@ -20,8 +20,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version version
created created
createdBy createdBy
createdByUser {
id,
email,
displayName
}
lastModified lastModified
lastModifiedBy lastModifiedBy
lastModifiedByUser {
id,
email,
displayName
}
url url
thumbnailUrl thumbnailUrl
sourceUrl sourceUrl
@ -54,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Created = now, Created = now,
CreatedBy = RefToken.User("user1"), CreatedBy = RefToken.User("user1"),
LastModified = now, LastModified = now,
LastModifiedBy = RefToken.User("user2"), LastModifiedBy = RefToken.Client("client1"),
FileName = "MyFile.png", FileName = "MyFile.png",
Slug = "myfile.png", Slug = "myfile.png",
FileSize = 1024, FileSize = 1024,
@ -85,8 +95,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version = asset.Version, version = asset.Version,
created = asset.Created, created = asset.Created,
createdBy = asset.CreatedBy.ToString(), createdBy = asset.CreatedBy.ToString(),
createdByUser = new
{
id = asset.CreatedBy.Identifier,
email = $"{asset.CreatedBy.Identifier}@email.com",
displayName = $"name_{asset.CreatedBy.Identifier}"
},
lastModified = asset.LastModified, lastModified = asset.LastModified,
lastModifiedBy = asset.LastModifiedBy.ToString(), lastModifiedBy = asset.LastModifiedBy.ToString(),
lastModifiedByUser = new
{
id = asset.LastModifiedBy.Identifier,
email = $"{asset.LastModifiedBy}",
displayName = asset.LastModifiedBy.Identifier
},
url = $"assets/{asset.AppId.Name}/{asset.Id}", url = $"assets/{asset.AppId.Name}/{asset.Id}",
thumbnailUrl = $"assets/{asset.AppId.Name}/{asset.Id}?width=100", thumbnailUrl = $"assets/{asset.AppId.Name}/{asset.Id}?width=100",
sourceUrl = $"assets/source/{asset.Id}", sourceUrl = $"assets/source/{asset.Id}",

28
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs

@ -20,8 +20,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
version version
created created
createdBy createdBy
createdByUser {
id,
email,
displayName
}
lastModified lastModified
lastModifiedBy lastModifiedBy
lastModifiedByUser {
id,
email,
displayName
}
status status
statusColor statusColor
url url
@ -148,7 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Created = now, Created = now,
CreatedBy = RefToken.User("user1"), CreatedBy = RefToken.User("user1"),
LastModified = now, LastModified = now,
LastModifiedBy = RefToken.User("user2"), LastModifiedBy = RefToken.Client("client1"),
Data = data, Data = data,
SchemaId = schemaId, SchemaId = schemaId,
Status = Status.Draft, Status = Status.Draft,
@ -192,9 +202,21 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
id = content.Id, id = content.Id,
version = 1, version = 1,
created = content.Created, created = content.Created,
createdBy = "subject:user1", createdBy = content.CreatedBy.ToString(),
createdByUser = new
{
id = content.CreatedBy.Identifier,
email = $"{content.CreatedBy.Identifier}@email.com",
displayName = $"name_{content.CreatedBy.Identifier}"
},
lastModified = content.LastModified, lastModified = content.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = content.LastModifiedBy.ToString(),
lastModifiedByUser = new
{
id = content.LastModifiedBy.Identifier,
email = $"{content.LastModifiedBy}",
displayName = content.LastModifiedBy.Identifier
},
status = "DRAFT", status = "DRAFT",
statusColor = "red", statusColor = "red",
url = $"contents/my-schema/{content.Id}", url = $"contents/my-schema/{content.Id}",

68
backend/tests/Squidex.Domain.Apps.Entities.Tests/Notifications/NotificationEmailSenderTests.cs

@ -6,16 +6,14 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Infrastructure.Email; using Squidex.Infrastructure.Email;
using Squidex.Log; using Squidex.Log;
using Squidex.Shared.Identity;
using Squidex.Shared.Users; using Squidex.Shared.Users;
using Xunit; using Xunit;
@ -25,11 +23,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
{ {
private readonly IEmailSender emailSender = A.Fake<IEmailSender>(); private readonly IEmailSender emailSender = A.Fake<IEmailSender>();
private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>(); private readonly IUrlGenerator urlGenerator = A.Fake<IUrlGenerator>();
private readonly IUser assigner = A.Fake<IUser>(); private readonly IUser assigner = UserMocks.User("1", "1@email.com", "user1");
private readonly IUser user = A.Fake<IUser>(); private readonly IUser assigned = UserMocks.User("2", "2@email.com", "user2");
private readonly ISemanticLog log = A.Fake<ISemanticLog>(); private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly List<Claim> assignerClaims = new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "Sebastian Stehle") };
private readonly List<Claim> assigneeClaims = new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "Qaisar Ahmad") };
private readonly string appName = "my-app"; private readonly string appName = "my-app";
private readonly string uiUrl = "my-ui"; private readonly string uiUrl = "my-ui";
private readonly NotificationEmailTextOptions texts = new NotificationEmailTextOptions(); private readonly NotificationEmailTextOptions texts = new NotificationEmailTextOptions();
@ -37,16 +33,6 @@ namespace Squidex.Domain.Apps.Entities.Notifications
public NotificationEmailSenderTests() public NotificationEmailSenderTests()
{ {
A.CallTo(() => assigner.Email)
.Returns("sebastian@squidex.io");
A.CallTo(() => assigner.Claims)
.Returns(assignerClaims);
A.CallTo(() => user.Email)
.Returns("qaisar@squidex.io");
A.CallTo(() => user.Claims)
.Returns(assigneeClaims);
A.CallTo(() => urlGenerator.UI()) A.CallTo(() => urlGenerator.UI())
.Returns(uiUrl); .Returns(uiUrl);
@ -56,25 +42,25 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact] [Fact]
public async Task Should_format_assigner_email_and_send_email() public async Task Should_format_assigner_email_and_send_email()
{ {
await TestInvitationFormattingAsync("Email: $ASSIGNER_EMAIL", "Email: sebastian@squidex.io"); await TestInvitationFormattingAsync("Email: $ASSIGNER_EMAIL", "Email: 1@email.com");
} }
[Fact] [Fact]
public async Task Should_format_assigner_name_and_send_email() public async Task Should_format_assigner_name_and_send_email()
{ {
await TestInvitationFormattingAsync("Name: $ASSIGNER_NAME", "Name: Sebastian Stehle"); await TestInvitationFormattingAsync("Name: $ASSIGNER_NAME", "Name: user1");
} }
[Fact] [Fact]
public async Task Should_format_user_email_and_send_email() public async Task Should_format_user_email_and_send_email()
{ {
await TestInvitationFormattingAsync("Email: $USER_EMAIL", "Email: qaisar@squidex.io"); await TestInvitationFormattingAsync("Email: $USER_EMAIL", "Email: 2@email.com");
} }
[Fact] [Fact]
public async Task Should_format_user_name_and_send_email() public async Task Should_format_user_name_and_send_email()
{ {
await TestInvitationFormattingAsync("Name: $USER_NAME", "Name: Qaisar Ahmad"); await TestInvitationFormattingAsync("Name: $USER_NAME", "Name: user2");
} }
[Fact] [Fact]
@ -104,9 +90,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact] [Fact]
public async Task Should_not_send_invitation_email_if_texts_for_new_user_are_empty() public async Task Should_not_send_invitation_email_if_texts_for_new_user_are_empty()
{ {
await sut.SendInviteAsync(assigner, user, appName); await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
MustLogWarning(); MustLogWarning();
@ -115,9 +101,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact] [Fact]
public async Task Should_not_send_invitation_email_if_texts_for_existing_user_are_empty() public async Task Should_not_send_invitation_email_if_texts_for_existing_user_are_empty()
{ {
await sut.SendInviteAsync(assigner, user, appName); await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
MustLogWarning(); MustLogWarning();
@ -126,25 +112,39 @@ namespace Squidex.Domain.Apps.Entities.Notifications
[Fact] [Fact]
public async Task Should_not_send_usage_email_if_texts_empty() public async Task Should_not_send_usage_email_if_texts_empty()
{ {
await sut.SendUsageAsync(user, appName, 100, 120); await sut.SendUsageAsync(assigned, appName, 100, 120);
A.CallTo(() => emailSender.SendAsync(user.Email, A<string>._, A<string>._, A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(assigned.Email, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
MustLogWarning(); MustLogWarning();
} }
[Fact]
public async Task Should_not_send_invitation_email_when_no_consent_given()
{
var withoutConsent = UserMocks.User("2", "2@email.com", "user", false);
texts.ExistingUserSubject = "email-subject";
texts.ExistingUserBody = "email-body";
await sut.SendInviteAsync(assigner, withoutConsent, appName);
A.CallTo(() => emailSender.SendAsync(withoutConsent.Email, "email-subject", "email-body", A<CancellationToken>._))
.MustNotHaveHappened();
}
[Fact] [Fact]
public async Task Should_send_invitation_email_when_consent_given() public async Task Should_send_invitation_email_when_consent_given()
{ {
assigneeClaims.Add(new Claim(SquidexClaimTypes.Consent, "True")); var withConsent = UserMocks.User("2", "2@email.com", "user", true);
texts.ExistingUserSubject = "email-subject"; texts.ExistingUserSubject = "email-subject";
texts.ExistingUserBody = "email-body"; texts.ExistingUserBody = "email-body";
await sut.SendInviteAsync(assigner, user, appName); await sut.SendInviteAsync(assigner, withConsent, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, "email-subject", "email-body", A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(withConsent.Email, "email-subject", "email-body", A<CancellationToken>._))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -153,9 +153,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
texts.UsageSubject = pattern; texts.UsageSubject = pattern;
texts.UsageBody = pattern; texts.UsageBody = pattern;
await sut.SendUsageAsync(user, appName, 100, 120); await sut.SendUsageAsync(assigned, appName, 100, 120);
A.CallTo(() => emailSender.SendAsync(user.Email, result, result, A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(assigned.Email, result, result, A<CancellationToken>._))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -164,9 +164,9 @@ namespace Squidex.Domain.Apps.Entities.Notifications
texts.NewUserSubject = pattern; texts.NewUserSubject = pattern;
texts.NewUserBody = pattern; texts.NewUserBody = pattern;
await sut.SendInviteAsync(assigner, user, appName); await sut.SendInviteAsync(assigner, assigned, appName);
A.CallTo(() => emailSender.SendAsync(user.Email, result, result, A<CancellationToken>._)) A.CallTo(() => emailSender.SendAsync(assigned.Email, result, result, A<CancellationToken>._))
.MustHaveHappened(); .MustHaveHappened();
} }

30
backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/Mocks.cs

@ -31,10 +31,17 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
var app = A.Fake<IAppEntity>(); var app = A.Fake<IAppEntity>();
A.CallTo(() => app.Id).Returns(appId.Id); A.CallTo(() => app.Id)
A.CallTo(() => app.Name).Returns(appId.Name); .Returns(appId.Id);
A.CallTo(() => app.Languages).Returns(config);
A.CallTo(() => app.UniqueId).Returns(appId.Id); A.CallTo(() => app.Name)
.Returns(appId.Name);
A.CallTo(() => app.Languages)
.Returns(config);
A.CallTo(() => app.UniqueId)
.Returns(appId.Id);
return app; return app;
} }
@ -43,10 +50,17 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
{ {
var schema = A.Fake<ISchemaEntity>(); var schema = A.Fake<ISchemaEntity>();
A.CallTo(() => schema.Id).Returns(schemaId.Id); A.CallTo(() => schema.Id)
A.CallTo(() => schema.AppId).Returns(appId); .Returns(schemaId.Id);
A.CallTo(() => schema.SchemaDef).Returns(schemaDef ?? new Schema(schemaId.Name));
A.CallTo(() => schema.UniqueId).Returns(DomainId.Combine(appId, schemaId.Id)); A.CallTo(() => schema.AppId)
.Returns(appId);
A.CallTo(() => schema.SchemaDef)
.Returns(schemaDef ?? new Schema(schemaId.Name));
A.CallTo(() => schema.UniqueId)
.Returns(DomainId.Combine(appId, schemaId.Id));
return schema; return schema;
} }

Loading…
Cancel
Save