Integration Tests: Avoid asserting on errors for permission tests (#20643)

* Added integration tests for PropertyTypeUsageService and adjusted assert in management API permissions test.

* Commented or fixed management API integration tests verifying permissions where we were asserting on an error response.
This commit is contained in:
Andy Butland
2025-11-11 06:59:25 +01:00
committed by GitHub
parent d8198d2f5c
commit cfa32b265a
13 changed files with 73 additions and 35 deletions

View File

@@ -1,7 +1,17 @@
namespace Umbraco.Cms.Core.Persistence.Repositories;
/// <summary>
/// Defines repository methods for querying property type usage.
/// </summary>
public interface IPropertyTypeUsageRepository
{
/// <summary>
/// Determines whether there are any saved property values for the specified content type and property alias.
/// </summary>
Task<bool> HasSavedPropertyValuesAsync(Guid contentTypeKey, string propertyAlias);
/// <summary>
/// Determines whether a content type with the specified unique identifier exists.
/// </summary>
Task<bool> ContentTypeExistAsync(Guid contentTypeKey);
}

View File

@@ -2,6 +2,9 @@ using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
/// <summary>
/// Defines service methods for querying property type usage.
/// </summary>
public interface IPropertyTypeUsageService
{
/// <summary>

View File

@@ -4,19 +4,25 @@ using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Core.Services;
/// <inheritdoc/>
public class PropertyTypeUsageService : IPropertyTypeUsageService
{
private readonly IPropertyTypeUsageRepository _propertyTypeUsageRepository;
private readonly IContentTypeService _contentTypeService;
private readonly ICoreScopeProvider _scopeProvider;
// TODO (V18): Remove IContentTypeService parameter from constructor.
/// <summary>
/// Initializes a new instance of the <see cref="PropertyTypeUsageService"/> class.
/// </summary>
public PropertyTypeUsageService(
IPropertyTypeUsageRepository propertyTypeUsageRepository,
#pragma warning disable IDE0060 // Remove unused parameter
IContentTypeService contentTypeService,
#pragma warning restore IDE0060 // Remove unused parameter
ICoreScopeProvider scopeProvider)
{
_propertyTypeUsageRepository = propertyTypeUsageRepository;
_contentTypeService = contentTypeService;
_scopeProvider = scopeProvider;
}

View File

@@ -1,4 +1,3 @@
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Persistence.Repositories;
@@ -8,28 +7,26 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
/// <inheritdoc/>
internal sealed class PropertyTypeUsageRepository : IPropertyTypeUsageRepository
{
private static readonly Guid?[] NodeObjectTypes = new Guid?[]
{
private static readonly List<Guid> _nodeObjectTypes =
[
Constants.ObjectTypes.DocumentType, Constants.ObjectTypes.MediaType, Constants.ObjectTypes.MemberType,
};
];
private readonly IScopeAccessor _scopeAccessor;
public PropertyTypeUsageRepository(IScopeAccessor scopeAccessor)
{
_scopeAccessor = scopeAccessor;
}
/// <summary>
/// Initializes a new instance of the <see cref="PropertyTypeUsageRepository"/> class.
/// </summary>
public PropertyTypeUsageRepository(IScopeAccessor scopeAccessor) => _scopeAccessor = scopeAccessor;
/// <inheritdoc/>
public Task<bool> HasSavedPropertyValuesAsync(Guid contentTypeKey, string propertyAlias)
{
IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database;
if (database is null)
{
throw new InvalidOperationException("A scope is required to query the database");
}
IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database
?? throw new InvalidOperationException("A scope is required to query the database");
Sql<ISqlContext> selectQuery = database.SqlContext.Sql()
.SelectAll()
@@ -47,26 +44,21 @@ internal sealed class PropertyTypeUsageRepository : IPropertyTypeUsageRepository
return Task.FromResult(database.ExecuteScalar<bool>(hasValuesQuery));
}
/// <inheritdoc/>
public Task<bool> ContentTypeExistAsync(Guid contentTypeKey)
{
IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database;
if (database is null)
{
throw new InvalidOperationException("A scope is required to query the database");
}
IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database
?? throw new InvalidOperationException("A scope is required to query the database");
Sql<ISqlContext> selectQuery = database.SqlContext.Sql()
.SelectAll()
.From<NodeDto>("n")
.Where<NodeDto>(n => n.UniqueId == contentTypeKey, "n")
.Where<NodeDto>(n => NodeObjectTypes.Contains(n.NodeObjectType), "n");
.WhereIn<NodeDto>(n => n.NodeObjectType, _nodeObjectTypes, "n");
Sql<ISqlContext> hasValuesQuery = database.SqlContext.Sql()
.SelectAnyIfExists(selectQuery);
return Task.FromResult(database.ExecuteScalar<bool>(hasValuesQuery));
}
}

View File

@@ -27,7 +27,7 @@ public class ExecuteActionHealthCheckControllerTests : ManagementApiUserGroupTes
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
@@ -58,7 +58,7 @@ public class ExecuteActionHealthCheckControllerTests : ManagementApiUserGroupTes
protected override async Task<HttpResponseMessage> ClientRequest()
{
HealthCheckActionRequestModel healthCheckActionRequest =
new() { HealthCheck = new ReferenceByIdModel(_dataIntegrityHealthCheckId), ValueRequired = false };
new() { HealthCheck = new ReferenceByIdModel(_dataIntegrityHealthCheckId), ValueRequired = false, Alias = "fixContentPaths" };
return await Client.PostAsync(Url, JsonContent.Create(healthCheckActionRequest));
}
}

View File

@@ -12,7 +12,7 @@ public class AllLogViewerControllerTests : ManagementApiUserGroupTestBase<AllLog
// We get the InternalServerError for the admin because it has access, but there is no log file to view
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -11,7 +11,7 @@ public class AllMessageTemplateLogViewerControllerTests : ManagementApiUserGroup
// We get the InternalServerError for the admin because it has access, but there is no log file to view
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -11,7 +11,7 @@ public class LogLevelCountLogViewerControllerTests : ManagementApiUserGroupTestB
// We get the InternalServerError for the admin because it has access, but there is no log file to view
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -11,7 +11,7 @@ public class ValidateLogFileSizeLogViewerControllerTests: ManagementApiUserGroup
// We get the InternalServerError for the admin because it has access, but there is no log file to view
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -36,7 +36,7 @@ public class IsUsedPropertyTypeControllerTests : ManagementApiUserGroupTestBase<
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError
ExpectedStatusCode = HttpStatusCode.OK
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -27,7 +27,7 @@ public class InviteUserControllerTests : ManagementApiUserGroupTestBase<InviteUs
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
{
ExpectedStatusCode = HttpStatusCode.InternalServerError,
ExpectedStatusCode = HttpStatusCode.InternalServerError, // We expect an error here because email sending is not configured in these tests.
};
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()

View File

@@ -1,7 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using NUnit.Framework;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
@@ -11,6 +10,8 @@ namespace Umbraco.Cms.Tests.Integration.Testing;
public abstract class UmbracoIntegrationTestWithContent : UmbracoIntegrationTest
{
protected const string TextpageContentTypeKey = "1D3A8E6E-2EA9-4CC1-B229-1AEE19821522";
protected const string TextpageKey = "B58B3AD4-62C2-4E27-B1BE-837BD7C533E0";
protected const string SubPageKey = "07EABF4A-5C62-4662-9F2A-15BBB488BCA5";
protected const string SubPage2Key = "0EED78FC-A6A8-4587-AB18-D3AFE212B1C4";
@@ -48,7 +49,7 @@ public abstract class UmbracoIntegrationTestWithContent : UmbracoIntegrationTest
// Create and Save ContentType "umbTextpage" -> 1051 (template), 1052 (content type)
ContentType =
ContentTypeBuilder.CreateSimpleContentType("umbTextpage", "Textpage", defaultTemplateId: template.Id);
ContentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522");
ContentType.Key = new Guid(TextpageContentTypeKey);
ContentTypeService.Save(ContentType);
// Create and Save Content "Homepage" based on "umbTextpage" -> 1053

View File

@@ -0,0 +1,26 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
internal sealed class PropertyTypeUsageServiceTests : UmbracoIntegrationTestWithContent
{
private IPropertyTypeUsageService PropertyTypeUsageService => GetRequiredService<IPropertyTypeUsageService>();
[TestCase(TextpageContentTypeKey, "title", true, true, PropertyTypeOperationStatus.Success)]
[TestCase("1D3A8E6E-2EA9-4CC1-B229-1AEE19821523", "title", false, false, PropertyTypeOperationStatus.ContentTypeNotFound)]
[TestCase(TextpageContentTypeKey, "missingProperty", true, false, PropertyTypeOperationStatus.Success)]
public async Task Can_Check_For_Saved_Property_Values(Guid contentTypeKey, string propertyAlias, bool expectedSuccess, bool expectedResult, PropertyTypeOperationStatus expectedOperationStatus)
{
Attempt<bool, PropertyTypeOperationStatus> resultAttempt = await PropertyTypeUsageService.HasSavedPropertyValuesAsync(contentTypeKey, propertyAlias);
Assert.AreEqual(expectedSuccess, resultAttempt.Success);
Assert.AreEqual(expectedResult, resultAttempt.Result);
Assert.AreEqual(expectedOperationStatus, resultAttempt.Status);
}
}