Merge branch 'v15/dev' into v16/merge-from-15
# Conflicts: # src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs # src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs # src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs # src/Umbraco.Core/Services/ContentEditingService.cs # src/Umbraco.Core/Services/DataTypeService.cs # src/Umbraco.Core/Services/IContentEditingService.cs # src/Umbraco.Core/Services/IDataTypeService.cs # src/Umbraco.Core/Services/ITrackedReferencesService.cs # src/Umbraco.Core/Services/RelationService.cs # src/Umbraco.Core/Services/TrackedReferencesService.cs # src/Umbraco.Infrastructure/Examine/Deferred/DeliveryApiContentIndexHandleContentTypeChanges.cs # src/Umbraco.Infrastructure/Examine/DeliveryApiIndexingHandler.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs # src/Umbraco.Web.UI.Client/src/external/backend-api/src/sdk.gen.ts # src/Umbraco.Web.UI.Client/src/mocks/data/document-blueprint/document-blueprint.data.ts # src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts # src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts # src/Umbraco.Web.UI.Client/src/packages/core/router/route/route.context.ts # src/Umbraco.Web.UI.Client/src/packages/core/router/route/route.interface.ts # src/Umbraco.Web.UI.Client/src/packages/core/router/route/router-slot.element.ts # src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot/model.ts # src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts # src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts # src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts # tests/Umbraco.Tests.AcceptanceTest/package-lock.json # tests/Umbraco.Tests.AcceptanceTest/package.json # tests/Umbraco.Tests.Common/Builders/UserGroupBuilder.cs # tests/Umbraco.Tests.Integration/Umbraco.Core/Services/TemporaryFileServiceTests.cs
This commit is contained in:
@@ -34,15 +34,15 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
services.AddTransient<IUserGroupPresentationFactory, UserGroupPresentationFactory>();
|
||||
services.AddSingleton<IPermissionPresentationFactory, PermissionPresentationFactory>();
|
||||
services.AddSingleton<DocumentPermissionMapper>();
|
||||
services.AddSingleton<IPermissionMapper>(x=>x.GetRequiredService<DocumentPermissionMapper>());
|
||||
services.AddSingleton<IPermissionPresentationMapper>(x=>x.GetRequiredService<DocumentPermissionMapper>());
|
||||
services.AddSingleton<IPermissionMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
|
||||
services.AddSingleton<IPermissionPresentationMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task Can_Map_Create_Model_And_Create()
|
||||
{
|
||||
var updateModel = new CreateUserGroupRequestModel()
|
||||
var createModel = new CreateUserGroupRequestModel()
|
||||
{
|
||||
Alias = "testAlias",
|
||||
FallbackPermissions = new HashSet<string>(),
|
||||
@@ -53,7 +53,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
Permissions = new HashSet<IPermissionPresentationModel>()
|
||||
};
|
||||
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
|
||||
Assert.IsTrue(attempt.Success);
|
||||
|
||||
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);
|
||||
@@ -71,7 +71,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
[Test]
|
||||
public async Task Cannot_Create_UserGroup_With_Unexisting_Document_Reference()
|
||||
{
|
||||
var updateModel = new CreateUserGroupRequestModel()
|
||||
var createModel = new CreateUserGroupRequestModel()
|
||||
{
|
||||
Alias = "testAlias",
|
||||
FallbackPermissions = new HashSet<string>(),
|
||||
@@ -89,7 +89,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
}
|
||||
};
|
||||
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
|
||||
Assert.IsTrue(attempt.Success);
|
||||
|
||||
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);
|
||||
@@ -102,11 +102,11 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_Usergroup_With_Empty_Granluar_Permissions_For_Document()
|
||||
public async Task Can_Create_Usergroup_With_Empty_Granular_Permissions_For_Document()
|
||||
{
|
||||
var contentKey = await CreateContent();
|
||||
|
||||
var updateModel = new CreateUserGroupRequestModel()
|
||||
var createModel = new CreateUserGroupRequestModel()
|
||||
{
|
||||
Alias = "testAlias",
|
||||
FallbackPermissions = new HashSet<string>(),
|
||||
@@ -124,7 +124,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
|
||||
}
|
||||
};
|
||||
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
|
||||
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
|
||||
Assert.IsTrue(attempt.Success);
|
||||
|
||||
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Mapping.Permissions;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Models.Membership.Permissions;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.Factories;
|
||||
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class UserPresentationFactoryTests : UmbracoIntegrationTestWithContent
|
||||
{
|
||||
public IUserPresentationFactory UserPresentationFactory => GetRequiredService<IUserPresentationFactory>();
|
||||
|
||||
public IUserGroupService UserGroupService => GetRequiredService<IUserGroupService>();
|
||||
|
||||
public IUserService UserService => GetRequiredService<IUserService>();
|
||||
|
||||
public ILanguageService LanguageService => GetRequiredService<ILanguageService>();
|
||||
|
||||
public IMediaService MediaService => GetRequiredService<IMediaService>();
|
||||
|
||||
protected override void ConfigureTestServices(IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IUserPresentationFactory, UserPresentationFactory>();
|
||||
services.AddTransient<IUserGroupPresentationFactory, UserGroupPresentationFactory>();
|
||||
services.AddSingleton<IAbsoluteUrlBuilder, DefaultAbsoluteUrlBuilder>();
|
||||
services.AddSingleton<IUrlAssembler, DefaultUrlAssembler>();
|
||||
services.AddSingleton<IPasswordConfigurationPresentationFactory, PasswordConfigurationPresentationFactory>();
|
||||
services.AddSingleton<IPermissionPresentationFactory, PermissionPresentationFactory>();
|
||||
services.AddSingleton<DocumentPermissionMapper>();
|
||||
services.AddSingleton<IPermissionMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
|
||||
services.AddSingleton<IPermissionPresentationMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_Current_User_Response_Model()
|
||||
{
|
||||
var daLanguage = new LanguageBuilder()
|
||||
.WithCultureInfo("da-DK")
|
||||
.Build();
|
||||
await LanguageService.CreateAsync(daLanguage, Constants.Security.SuperUserKey);
|
||||
var enUsLanguage = await LanguageService.GetAsync("en-US");
|
||||
var daDkLanguage = await LanguageService.GetAsync("da-DK");
|
||||
|
||||
var rootContentKey = Guid.Parse(TextpageKey);
|
||||
var subPageContentKey = Guid.Parse(SubPageKey);
|
||||
var subPage2ContentKey = Guid.Parse(SubPage2Key);
|
||||
|
||||
var rootMediaFolder = MediaService.CreateMedia("Pictures Folder", Constants.System.Root, "Folder");
|
||||
MediaService.Save(rootMediaFolder);
|
||||
|
||||
var groupOne = await CreateUserGroup(
|
||||
"Group One",
|
||||
"groupOne",
|
||||
[enUsLanguage.Id],
|
||||
["A", "B", "C"],
|
||||
[
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = rootContentKey,
|
||||
Permission = "A",
|
||||
},
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = rootContentKey,
|
||||
Permission = "E",
|
||||
},
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = subPageContentKey,
|
||||
Permission = "F",
|
||||
},
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = subPage2ContentKey,
|
||||
Permission = "F",
|
||||
}
|
||||
],
|
||||
rootMediaFolder.Id);
|
||||
var groupTwo = await CreateUserGroup(
|
||||
"Group Two",
|
||||
"groupTwo",
|
||||
[daDkLanguage.Id],
|
||||
["A", "B", "D"],
|
||||
[
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = subPage2ContentKey,
|
||||
Permission = "G",
|
||||
},
|
||||
new DocumentGranularPermission
|
||||
{
|
||||
Key = subPage2ContentKey,
|
||||
Permission = "H",
|
||||
}
|
||||
],
|
||||
rootMediaFolder.Id);
|
||||
|
||||
var user = await CreateUser([groupOne.Key, groupTwo.Key]);
|
||||
|
||||
var model = await UserPresentationFactory.CreateCurrentUserResponseModelAsync(user);
|
||||
|
||||
Assert.AreEqual(user.Key, model.Id);
|
||||
Assert.AreEqual("test@test.com", model.Email);
|
||||
Assert.AreEqual("Test User", model.Name);
|
||||
Assert.AreEqual("test@test.com", model.UserName);
|
||||
Assert.AreEqual(2, model.UserGroupIds.Count);
|
||||
Assert.IsTrue(model.UserGroupIds.Select(x => x.Id).ContainsAll([groupOne.Key, groupTwo.Key]));
|
||||
Assert.IsFalse(model.HasAccessToAllLanguages);
|
||||
Assert.AreEqual(2, model.Languages.Count());
|
||||
Assert.IsTrue(model.Languages.ContainsAll(["en-US", "da-DK"]));
|
||||
Assert.IsTrue(model.HasDocumentRootAccess);
|
||||
Assert.AreEqual(0, model.DocumentStartNodeIds.Count);
|
||||
Assert.IsFalse(model.HasMediaRootAccess);
|
||||
Assert.AreEqual(1, model.MediaStartNodeIds.Count);
|
||||
Assert.AreEqual(rootMediaFolder.Key, model.MediaStartNodeIds.First().Id);
|
||||
Assert.IsFalse(model.HasAccessToSensitiveData);
|
||||
Assert.AreEqual(4, model.FallbackPermissions.Count);
|
||||
Assert.IsTrue(model.FallbackPermissions.ContainsAll(["A", "B", "C", "D"]));
|
||||
|
||||
// When aggregated, we expect one permission per document (we have several granular permissions assigned, for three unique documents).
|
||||
Assert.AreEqual(3, model.Permissions.Count);
|
||||
|
||||
// User has two user groups, one of which provides granular permissions for the root content item.
|
||||
// As such we expect the aggregated permissions to be the union of the specific permissions coming from the user group with them assigned to the document,
|
||||
// and the fallback permissions from the other.
|
||||
var rootContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == rootContentKey);
|
||||
Assert.AreEqual(4, rootContentPermissions.Verbs.Count);
|
||||
Assert.IsTrue(rootContentPermissions.Verbs.ContainsAll(["A", "B", "D", "E"]));
|
||||
|
||||
// The sub-page and it's parent have specific granular permissions from one user group.
|
||||
// So we expect the aggregated permissions to include those from the sub-page and the other user's groups fallback permissions.
|
||||
var subPageContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == subPageContentKey);
|
||||
Assert.AreEqual(4, subPageContentPermissions.Verbs.Count);
|
||||
Assert.IsTrue(subPageContentPermissions.Verbs.ContainsAll(["A", "B", "D", "F"]));
|
||||
|
||||
// Both user groups provide granular permissions for the second sub-page content item.
|
||||
// Here we expect the aggregated permissions to be the union of the granular permissions on the document from both user groups.
|
||||
var subPage2ContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == subPage2ContentKey);
|
||||
Assert.AreEqual(3, subPage2ContentPermissions.Verbs.Count);
|
||||
Assert.IsTrue(subPage2ContentPermissions.Verbs.ContainsAll(["F", "G", "H"]));
|
||||
}
|
||||
|
||||
private async Task<IUserGroup> CreateUserGroup(
|
||||
string name,
|
||||
string alias,
|
||||
int[] allowedLanguages,
|
||||
string[] permissions,
|
||||
DocumentGranularPermission[] granularPermissions,
|
||||
int startMediaId)
|
||||
{
|
||||
var userGroup = new UserGroupBuilder()
|
||||
.WithName(name)
|
||||
.WithAlias(alias)
|
||||
.WithAllowedLanguages(allowedLanguages)
|
||||
.WithStartMediaId(startMediaId)
|
||||
.WithPermissions(permissions.ToHashSet())
|
||||
.WithGranularPermissions(granularPermissions)
|
||||
.Build();
|
||||
var createUserGroupResult = await UserGroupService.CreateAsync(userGroup, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(createUserGroupResult.Success);
|
||||
return userGroup;
|
||||
}
|
||||
|
||||
private async Task<IUser> CreateUser(Guid[] userGroupKeys)
|
||||
{
|
||||
var createUserAttempt = await UserService.CreateAsync(Constants.Security.SuperUserKey, new UserCreateModel
|
||||
{
|
||||
Email = "test@test.com",
|
||||
Name = "Test User",
|
||||
UserName = "test@test.com",
|
||||
UserGroupKeys = userGroupKeys.ToHashSet(),
|
||||
});
|
||||
Assert.IsTrue(createUserAttempt.Success);
|
||||
|
||||
return await UserService.GetAsync(createUserAttempt.Result.CreatedUser.Key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
public class ApiContentPathResolverInvariantTests : ApiContentPathResolverTestBase
|
||||
{
|
||||
private Dictionary<string, IContent> _contentByName = new ();
|
||||
|
||||
public static void ConfigureIncludeTopLevelNodeInPath(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<GlobalSettings>(config => config.HideTopLevelNodeFromPath = false);
|
||||
|
||||
[SetUp]
|
||||
public async Task SetUpTest()
|
||||
{
|
||||
UmbracoContextFactory.EnsureUmbracoContext();
|
||||
SetRequestHost("localhost");
|
||||
|
||||
if (_contentByName.Any())
|
||||
{
|
||||
// these tests all run on the same DB to make them run faster, so we need to get the cache in a
|
||||
// predictable state with each test run.
|
||||
RefreshContentCache();
|
||||
return;
|
||||
}
|
||||
|
||||
await DocumentUrlService.InitAsync(true, CancellationToken.None);
|
||||
|
||||
var contentType = new ContentTypeBuilder()
|
||||
.WithAlias("theContentType")
|
||||
.Build();
|
||||
contentType.AllowedAsRoot = true;
|
||||
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
contentType.AllowedContentTypes = [new() { Alias = contentType.Alias, Key = contentType.Key }];
|
||||
await ContentTypeService.UpdateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
foreach (var rootNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var root = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithName($"Root {rootNumber}")
|
||||
.Build();
|
||||
ContentService.Save(root);
|
||||
ContentService.Publish(root, ["*"]);
|
||||
_contentByName[root.Name!] = root;
|
||||
|
||||
foreach (var childNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var child = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(root)
|
||||
.WithName($"Child {childNumber}")
|
||||
.Build();
|
||||
ContentService.Save(child);
|
||||
ContentService.Publish(child, ["*"]);
|
||||
_contentByName[$"{root.Name!}/{child.Name!}"] = child;
|
||||
|
||||
foreach (var grandchildNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var grandchild = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(child)
|
||||
.WithName($"Grandchild {grandchildNumber}")
|
||||
.Build();
|
||||
ContentService.Save(grandchild);
|
||||
ContentService.Publish(grandchild, ["*"]);
|
||||
_contentByName[$"{root.Name!}/{child.Name!}/{grandchild.Name!}"] = grandchild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void First_Root_Without_StartItem()
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName["Root 1"].Key, content.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void First_Root_Without_StartItem_With_Top_Level_Node_Included()
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName["Root 1"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void First_Root_Child_Without_StartItem(int child)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root 1/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1)]
|
||||
[TestCase(2, 2)]
|
||||
[TestCase(3, 3)]
|
||||
[TestCase(1, 2)]
|
||||
[TestCase(2, 3)]
|
||||
[TestCase(3, 1)]
|
||||
public void First_Root_Grandchild_Without_StartItem(int child, int grandchild)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}/grandchild-{grandchild}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root 1/Child {child}/Grandchild {grandchild}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void Root_With_StartItem(int root)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Root_With_StartItem_With_Top_Level_Node_Included(int root)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1)]
|
||||
[TestCase(2, 2)]
|
||||
[TestCase(3, 3)]
|
||||
[TestCase(1, 2)]
|
||||
[TestCase(2, 3)]
|
||||
[TestCase(3, 1)]
|
||||
public void Child_With_StartItem(int root, int child)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1)]
|
||||
[TestCase(2, 2)]
|
||||
[TestCase(3, 3)]
|
||||
[TestCase(1, 2)]
|
||||
[TestCase(2, 3)]
|
||||
[TestCase(3, 1)]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Child_With_StartItem_With_Top_Level_Node_Included(int root, int child)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1, 1)]
|
||||
[TestCase(2, 2, 2)]
|
||||
[TestCase(3, 3, 3)]
|
||||
[TestCase(1, 2, 3)]
|
||||
[TestCase(2, 3, 1)]
|
||||
[TestCase(3, 1, 2)]
|
||||
public void Grandchild_With_StartItem(int root, int child, int grandchild)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}/grandchild-{grandchild}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}/Grandchild {grandchild}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/", 1)]
|
||||
[TestCase("/root-2", 2)]
|
||||
[TestCase("/root-3", 3)]
|
||||
public void Root_By_Path_With_StartItem(string path, int root)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/", 1)]
|
||||
[TestCase("/root-2", 2)]
|
||||
[TestCase("/root-3", 3)]
|
||||
public void Root_By_Path_Without_StartItem(string path, int root)
|
||||
{
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public async Task Root_With_Domain_Bindings(int root)
|
||||
{
|
||||
await SetContentHost(_contentByName[$"Root {root}"], "some.host", "en-US");
|
||||
SetRequestHost("some.host");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/a", 1)]
|
||||
[TestCase("/123", 2)]
|
||||
[TestCase("/no-such-child", 3)]
|
||||
[TestCase("/a/b", 1)]
|
||||
[TestCase("/123/456", 2)]
|
||||
[TestCase("/no-such-child/no-such-grandchild", 3)]
|
||||
public void Non_Existant_Descendant_By_Path_With_StartItem(string path, int root)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNull(content);
|
||||
}
|
||||
|
||||
[TestCase("/a")]
|
||||
[TestCase("/123")]
|
||||
[TestCase("/a/b")]
|
||||
[TestCase("/123/456")]
|
||||
public void Non_Existant_Descendant_By_Path_Without_StartItem(string path)
|
||||
{
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNull(content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
|
||||
public abstract class ApiContentPathResolverTestBase : ApiContentRequestTestBase
|
||||
{
|
||||
protected IApiContentPathResolver ApiContentPathResolver => GetRequiredService<IApiContentPathResolver>();
|
||||
|
||||
protected IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
public class ApiContentPathResolverVariantTests : ApiContentPathResolverTestBase
|
||||
{
|
||||
private readonly Dictionary<string, IContent> _contentByName = new ();
|
||||
|
||||
public static void ConfigureIncludeTopLevelNodeInPath(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<GlobalSettings>(config => config.HideTopLevelNodeFromPath = false);
|
||||
|
||||
[SetUp]
|
||||
public async Task SetUpTest()
|
||||
{
|
||||
UmbracoContextFactory.EnsureUmbracoContext();
|
||||
SetRequestHost("localhost");
|
||||
|
||||
if (_contentByName.Any())
|
||||
{
|
||||
// these tests all run on the same DB to make them run faster, so we need to get the cache in a
|
||||
// predictable state with each test run.
|
||||
RefreshContentCache();
|
||||
return;
|
||||
}
|
||||
|
||||
await DocumentUrlService.InitAsync(true, CancellationToken.None);
|
||||
|
||||
await GetRequiredService<ILanguageService>().CreateAsync(new Language("da-DK", "Danish"), Constants.Security.SuperUserKey);
|
||||
|
||||
var contentType = new ContentTypeBuilder()
|
||||
.WithAlias("theContentType")
|
||||
.WithContentVariation(ContentVariation.Culture)
|
||||
.Build();
|
||||
contentType.AllowedAsRoot = true;
|
||||
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
contentType.AllowedContentTypes = [new() { Alias = contentType.Alias, Key = contentType.Key }];
|
||||
await ContentTypeService.UpdateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
foreach (var rootNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var root = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithCultureName("en-US", $"Root {rootNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Root {rootNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(root);
|
||||
ContentService.Publish(root, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}"] = root;
|
||||
|
||||
foreach (var childNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var child = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(root)
|
||||
.WithCultureName("en-US", $"Child {childNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Child {childNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(child);
|
||||
ContentService.Publish(child, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}/Child {childNumber}"] = child;
|
||||
|
||||
foreach (var grandchildNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var grandchild = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(child)
|
||||
.WithCultureName("en-US", $"Grandchild {grandchildNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Grandchild {grandchildNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(grandchild);
|
||||
ContentService.Publish(grandchild, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}/Child {childNumber}/Grandchild {grandchildNumber}"] = grandchild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("en-US")]
|
||||
[TestCase("da-DK")]
|
||||
public void First_Root_Without_StartItem(string culture)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName["Root 1"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("en-US")]
|
||||
[TestCase("da-DK")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void First_Root_Without_StartItem_With_Top_Level_Node_Included(string culture)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName["Root 1"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void First_Root_Child_Without_StartItem(int child, string culture)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}-{culture.ToLowerInvariant()}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root 1/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1, "en-US")]
|
||||
[TestCase(2, 2, "en-US")]
|
||||
[TestCase(3, 3, "en-US")]
|
||||
[TestCase(1, 2, "en-US")]
|
||||
[TestCase(2, 3, "en-US")]
|
||||
[TestCase(3, 1, "en-US")]
|
||||
[TestCase(1, 1, "da-DK")]
|
||||
[TestCase(2, 2, "da-DK")]
|
||||
[TestCase(3, 3, "da-DK")]
|
||||
[TestCase(1, 2, "da-DK")]
|
||||
[TestCase(2, 3, "da-DK")]
|
||||
[TestCase(3, 1, "da-DK")]
|
||||
public void First_Root_Grandchild_Without_StartItem(int child, int grandchild, string culture)
|
||||
{
|
||||
Assert.IsEmpty(GetRequiredService<IHttpContextAccessor>().HttpContext!.Request.Headers["Start-Item"].ToString());
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}-{culture.ToLowerInvariant()}/grandchild-{grandchild}-{culture.ToLowerInvariant()}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root 1/Child {child}/Grandchild {grandchild}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void Root_With_StartItem(int root, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "da-DK")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Root_With_StartItem_With_Top_Level_Node_Included(int root, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1, "en-US")]
|
||||
[TestCase(2, 2, "en-US")]
|
||||
[TestCase(3, 3, "en-US")]
|
||||
[TestCase(1, 2, "en-US")]
|
||||
[TestCase(2, 3, "en-US")]
|
||||
[TestCase(3, 1, "en-US")]
|
||||
[TestCase(1, 1, "da-DK")]
|
||||
[TestCase(2, 2, "da-DK")]
|
||||
[TestCase(3, 3, "da-DK")]
|
||||
[TestCase(1, 2, "da-DK")]
|
||||
[TestCase(2, 3, "da-DK")]
|
||||
[TestCase(3, 1, "da-DK")]
|
||||
public void Child_With_StartItem(int root, int child, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}-{culture.ToLowerInvariant()}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1, "en-US")]
|
||||
[TestCase(2, 2, "en-US")]
|
||||
[TestCase(3, 3, "en-US")]
|
||||
[TestCase(1, 2, "en-US")]
|
||||
[TestCase(2, 3, "en-US")]
|
||||
[TestCase(3, 1, "en-US")]
|
||||
[TestCase(1, 1, "da-DK")]
|
||||
[TestCase(2, 2, "da-DK")]
|
||||
[TestCase(3, 3, "da-DK")]
|
||||
[TestCase(1, 2, "da-DK")]
|
||||
[TestCase(2, 3, "da-DK")]
|
||||
[TestCase(3, 1, "da-DK")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Child_With_StartItem_With_Top_Level_Node_Included(int root, int child, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}-{culture.ToLowerInvariant()}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, 1, 1, "en-US")]
|
||||
[TestCase(2, 2, 2, "en-US")]
|
||||
[TestCase(3, 3, 3, "en-US")]
|
||||
[TestCase(1, 2, 3, "en-US")]
|
||||
[TestCase(2, 3, 1, "en-US")]
|
||||
[TestCase(3, 1, 2, "en-US")]
|
||||
[TestCase(1, 1, 1, "da-DK")]
|
||||
[TestCase(2, 2, 2, "da-DK")]
|
||||
[TestCase(3, 3, 3, "da-DK")]
|
||||
[TestCase(1, 2, 3, "da-DK")]
|
||||
[TestCase(2, 3, 1, "da-DK")]
|
||||
[TestCase(3, 1, 2, "da-DK")]
|
||||
public void Grandchild_With_StartItem(int root, int child, int grandchild, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath($"/child-{child}-{culture.ToLowerInvariant()}/grandchild-{grandchild}-{culture.ToLowerInvariant()}");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}/Child {child}/Grandchild {grandchild}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/", 1, "en-US")]
|
||||
[TestCase("/root-2-en-us", 2, "en-US")]
|
||||
[TestCase("/root-3-en-us", 3, "en-US")]
|
||||
[TestCase("/", 1, "da-DK")]
|
||||
[TestCase("/root-2-da-dk", 2, "da-DK")]
|
||||
[TestCase("/root-3-da-dk", 3, "da-DK")]
|
||||
public void Root_By_Path_With_StartItem(string path, int root, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/", 1, "en-US")]
|
||||
[TestCase("/root-2-en-us", 2, "en-US")]
|
||||
[TestCase("/root-3-en-us", 3, "en-US")]
|
||||
[TestCase("/", 1, "da-DK")]
|
||||
[TestCase("/root-2-da-dk", 2, "da-DK")]
|
||||
[TestCase("/root-3-da-dk", 3, "da-DK")]
|
||||
public void Root_By_Path_Without_StartItem(string path, int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public async Task Root_With_Domain_Bindings(int root, string culture)
|
||||
{
|
||||
await SetContentHost(_contentByName[$"Root {root}"], "some.host", "en-US");
|
||||
SetRequestHost("some.host");
|
||||
SetVariationContext(culture);
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath("/");
|
||||
Assert.IsNotNull(content);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, content.Key);
|
||||
}
|
||||
|
||||
[TestCase("/a", 1, "en-US")]
|
||||
[TestCase("/b", 1, "da-DK")]
|
||||
[TestCase("/123", 2, "en-US")]
|
||||
[TestCase("/456", 2, "da-DK")]
|
||||
[TestCase("/no-such-child", 3, "en-US")]
|
||||
[TestCase("/not-at-all", 3, "da-DK")]
|
||||
[TestCase("/a/b", 1, "en-US")]
|
||||
[TestCase("/c/d", 1, "da-DK")]
|
||||
[TestCase("/123/456", 2, "en-US")]
|
||||
[TestCase("/789/012", 2, "da-DK")]
|
||||
[TestCase("/no-such-child/no-such-grandchild", 3, "en-US")]
|
||||
[TestCase("/not-at-all/aint-no-way", 3, "da-DK")]
|
||||
public void Non_Existant_Descendant_By_Path_With_StartItem(string path, int root, string culture)
|
||||
{
|
||||
SetRequestStartItem($"root-{root}-{culture.ToLowerInvariant()}");
|
||||
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNull(content);
|
||||
}
|
||||
|
||||
[TestCase("/a")]
|
||||
[TestCase("/123")]
|
||||
[TestCase("/a/b")]
|
||||
[TestCase("/123/456")]
|
||||
public void Non_Existant_Descendant_By_Path_Without_StartItem(string path)
|
||||
{
|
||||
var content = ApiContentPathResolver.ResolveContentPath(path);
|
||||
Assert.IsNull(content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
public abstract class ApiContentRequestTestBase : UmbracoIntegrationTest
|
||||
{
|
||||
protected IContentService ContentService => GetRequiredService<IContentService>();
|
||||
|
||||
protected IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
|
||||
|
||||
protected IApiContentRouteBuilder ApiContentRouteBuilder => GetRequiredService<IApiContentRouteBuilder>();
|
||||
|
||||
protected IVariationContextAccessor VariationContextAccessor => GetRequiredService<IVariationContextAccessor>();
|
||||
|
||||
protected IUmbracoContextAccessor UmbracoContextAccessor => GetRequiredService<IUmbracoContextAccessor>();
|
||||
|
||||
protected IUmbracoContextFactory UmbracoContextFactory => GetRequiredService<IUmbracoContextFactory>();
|
||||
|
||||
protected override void CustomTestSetup(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.AddUmbracoHybridCache();
|
||||
builder.AddNotificationHandler<ContentTreeChangeNotification, ContentTreeChangeDistributedCacheNotificationHandler>();
|
||||
builder.Services.AddUnique<IServerMessenger, ContentEventsTests.LocalServerMessenger>();
|
||||
|
||||
builder.AddDeliveryApi();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task CleanUpAfterTest()
|
||||
{
|
||||
var domainService = GetRequiredService<IDomainService>();
|
||||
foreach (var content in ContentService.GetRootContent())
|
||||
{
|
||||
await domainService.UpdateDomainsAsync(content.Key, new DomainsUpdateModel { Domains = [] });
|
||||
}
|
||||
|
||||
var httpContextAccessor = GetRequiredService<IHttpContextAccessor>();
|
||||
httpContextAccessor.HttpContext?.Request.Headers.Clear();
|
||||
}
|
||||
|
||||
protected void SetVariationContext(string? culture)
|
||||
=> VariationContextAccessor.VariationContext = new VariationContext(culture: culture);
|
||||
|
||||
protected async Task SetContentHost(IContent content, string host, string culture)
|
||||
=> await GetRequiredService<IDomainService>().UpdateDomainsAsync(
|
||||
content.Key,
|
||||
new DomainsUpdateModel { Domains = [new DomainModel { DomainName = host, IsoCode = culture }] });
|
||||
|
||||
protected void SetRequestHost(string host)
|
||||
{
|
||||
var httpContextAccessor = GetRequiredService<IHttpContextAccessor>();
|
||||
|
||||
httpContextAccessor.HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Scheme = "https",
|
||||
Host = new HostString(host),
|
||||
Path = "/",
|
||||
QueryString = new QueryString(string.Empty)
|
||||
},
|
||||
RequestServices = Services
|
||||
};
|
||||
}
|
||||
|
||||
protected void SetRequestStartItem(string startItem)
|
||||
{
|
||||
var httpContextAccessor = GetRequiredService<IHttpContextAccessor>();
|
||||
if (httpContextAccessor.HttpContext is null)
|
||||
{
|
||||
throw new InvalidOperationException("HTTP context is null");
|
||||
}
|
||||
|
||||
httpContextAccessor.HttpContext.Request.Headers["Start-Item"] = startItem;
|
||||
}
|
||||
|
||||
protected void RefreshContentCache()
|
||||
=> Services.GetRequiredService<ContentCacheRefresher>().Refresh([new ContentCacheRefresher.JsonPayload { ChangeTypes = TreeChangeTypes.RefreshAll }]);
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
public class ApiContentRouteBuilderInvariantTests : ApiContentRouteBuilderTestBase
|
||||
{
|
||||
private readonly Dictionary<string, IContent> _contentByName = new ();
|
||||
|
||||
public static void ConfigureIncludeTopLevelNodeInPath(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<GlobalSettings>(config => config.HideTopLevelNodeFromPath = false);
|
||||
|
||||
public static void ConfigureOmitTrailingSlash(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<RequestHandlerSettings>(config => config.AddTrailingSlash = false);
|
||||
|
||||
[SetUp]
|
||||
public async Task SetUpTest()
|
||||
{
|
||||
SetRequestHost("localhost");
|
||||
|
||||
if (_contentByName.Any())
|
||||
{
|
||||
// these tests all run on the same DB to make them run faster, so we need to get the cache in a
|
||||
// predictable state with each test run.
|
||||
RefreshContentCache();
|
||||
return;
|
||||
}
|
||||
|
||||
var contentType = new ContentTypeBuilder()
|
||||
.WithAlias("theContentType")
|
||||
.Build();
|
||||
contentType.AllowedAsRoot = true;
|
||||
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
contentType.AllowedContentTypes = [new() { Alias = contentType.Alias, Key = contentType.Key }];
|
||||
await ContentTypeService.UpdateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
foreach (var rootNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var root = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithName($"Root {rootNumber}")
|
||||
.Build();
|
||||
ContentService.Save(root);
|
||||
ContentService.Publish(root, ["*"]);
|
||||
_contentByName[root.Name!] = root;
|
||||
|
||||
foreach (var childNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var child = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(root)
|
||||
.WithName($"Child {childNumber}")
|
||||
.Build();
|
||||
ContentService.Save(child);
|
||||
ContentService.Publish(child, ["*"]);
|
||||
_contentByName[$"{root.Name!}/{child.Name!}"] = child;
|
||||
|
||||
foreach (var grandchildNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var grandchild = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(child)
|
||||
.WithName($"Grandchild {grandchildNumber}")
|
||||
.Build();
|
||||
ContentService.Save(grandchild);
|
||||
ContentService.Publish(grandchild, ["*"]);
|
||||
_contentByName[$"{root.Name!}/{child.Name!}/{grandchild.Name!}"] = grandchild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void First_Root()
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName["Root 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual("root-1", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName["Root 1"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Last_Root()
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName["Root 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/root-3/", route.Path);
|
||||
Assert.AreEqual("root-3", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName["Root 3"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void First_Child(int root)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/child-1/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void Last_Child(int root)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/child-3/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void First_Grandchild(int root)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 1/Grandchild 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/child-1/grandchild-1/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void Last_Grandchild(int root)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 3/Grandchild 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/child-3/grandchild-3/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Root_With_Top_Level_Node_Included(int root)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public async Task Root_With_Domain_Bindings(int root)
|
||||
{
|
||||
await SetContentHost(_contentByName[$"Root {root}"], "some.host", "en-US");
|
||||
SetRequestHost("some.host");
|
||||
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "/")]
|
||||
[TestCase(2, "/root-2")]
|
||||
[TestCase(3, "/root-3")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureOmitTrailingSlash))]
|
||||
public void Root_Without_Trailing_Slash(int root, string expectedPath)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedPath, route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, 1)]
|
||||
[TestCase(2, 2)]
|
||||
[TestCase(3, 3)]
|
||||
[TestCase(1, 2)]
|
||||
[TestCase(2, 3)]
|
||||
[TestCase(3, 1)]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureOmitTrailingSlash))]
|
||||
public void Child_Without_Trailing_Slash(int root, int child)
|
||||
{
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child {child}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-{child}", route.Path);
|
||||
Assert.AreEqual($"root-{root}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
|
||||
public abstract class ApiContentRouteBuilderTestBase : ApiContentRequestTestBase
|
||||
{
|
||||
protected IPublishedContent GetPublishedContent(Guid key)
|
||||
{
|
||||
UmbracoContextAccessor.Clear();
|
||||
var umbracoContext = UmbracoContextFactory.EnsureUmbracoContext().UmbracoContext;
|
||||
var publishedContent = umbracoContext.Content?.GetById(key);
|
||||
Assert.IsNotNull(publishedContent);
|
||||
|
||||
return publishedContent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi.Request;
|
||||
|
||||
public class ApiContentRouteBuilderVariantTests : ApiContentRouteBuilderTestBase
|
||||
{
|
||||
private readonly Dictionary<string, IContent> _contentByName = new ();
|
||||
|
||||
public static void ConfigureIncludeTopLevelNodeInPath(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<GlobalSettings>(config => config.HideTopLevelNodeFromPath = false);
|
||||
|
||||
public static void ConfigureOmitTrailingSlash(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<RequestHandlerSettings>(config => config.AddTrailingSlash = false);
|
||||
|
||||
[SetUp]
|
||||
public async Task SetUpTest()
|
||||
{
|
||||
SetRequestHost("localhost");
|
||||
|
||||
if (_contentByName.Any())
|
||||
{
|
||||
// these tests all run on the same DB to make them run faster, so we need to get the cache in a
|
||||
// predictable state with each test run.
|
||||
RefreshContentCache();
|
||||
return;
|
||||
}
|
||||
|
||||
await GetRequiredService<ILanguageService>().CreateAsync(new Language("da-DK", "Danish"), Constants.Security.SuperUserKey);
|
||||
|
||||
var contentType = new ContentTypeBuilder()
|
||||
.WithAlias("theContentType")
|
||||
.WithContentVariation(ContentVariation.Culture)
|
||||
.Build();
|
||||
contentType.AllowedAsRoot = true;
|
||||
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
contentType.AllowedContentTypes = [new() { Alias = contentType.Alias, Key = contentType.Key }];
|
||||
await ContentTypeService.UpdateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
foreach (var rootNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var root = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithCultureName("en-US", $"Root {rootNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Root {rootNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(root);
|
||||
ContentService.Publish(root, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}"] = root;
|
||||
|
||||
foreach (var childNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var child = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(root)
|
||||
.WithCultureName("en-US", $"Child {childNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Child {childNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(child);
|
||||
ContentService.Publish(child, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}/Child {childNumber}"] = child;
|
||||
|
||||
foreach (var grandchildNumber in Enumerable.Range(1, 3))
|
||||
{
|
||||
var grandchild = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithParent(child)
|
||||
.WithCultureName("en-US", $"Grandchild {grandchildNumber} en-US")
|
||||
.WithCultureName("da-DK", $"Grandchild {grandchildNumber} da-DK")
|
||||
.Build();
|
||||
ContentService.Save(grandchild);
|
||||
ContentService.Publish(grandchild, ["*"]);
|
||||
_contentByName[$"Root {rootNumber}/Child {childNumber}/Grandchild {grandchildNumber}"] = grandchild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("da-DK")]
|
||||
[TestCase("en-US")]
|
||||
public void First_Root(string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual($"root-1-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root 1"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase("da-DK")]
|
||||
[TestCase("en-US")]
|
||||
public void Last_Root(string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName["Root 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/root-3-{culture.ToLowerInvariant()}/", route.Path);
|
||||
Assert.AreEqual($"root-3-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName["Root 3"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void First_Child(int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-1-{culture.ToLowerInvariant()}/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void Last_Child(int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-3-{culture.ToLowerInvariant()}/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void First_Grandchild(int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 1/Grandchild 1"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-1-{culture.ToLowerInvariant()}/grandchild-1-{culture.ToLowerInvariant()}/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public void Last_Grandchild(int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child 3/Grandchild 3"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-3-{culture.ToLowerInvariant()}/grandchild-3-{culture.ToLowerInvariant()}/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureIncludeTopLevelNodeInPath))]
|
||||
public void Root_With_Top_Level_Node_Included(int root, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US")]
|
||||
[TestCase(1, "da-DK")]
|
||||
[TestCase(2, "en-US")]
|
||||
[TestCase(2, "da-DK")]
|
||||
[TestCase(3, "en-US")]
|
||||
[TestCase(3, "da-DK")]
|
||||
public async Task Root_With_Domain_Bindings(int root, string culture)
|
||||
{
|
||||
await SetContentHost(_contentByName[$"Root {root}"], "some.host", culture);
|
||||
SetRequestHost("some.host");
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("/", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, "en-US", "/")]
|
||||
[TestCase(1, "da-DK", "/")]
|
||||
[TestCase(2, "en-US", "/root-2-en-us")]
|
||||
[TestCase(2, "da-DK", "/root-2-da-dk")]
|
||||
[TestCase(3, "en-US", "/root-3-en-us")]
|
||||
[TestCase(3, "da-DK", "/root-3-da-dk")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureOmitTrailingSlash))]
|
||||
public void Root_Without_Trailing_Slash(int root, string culture, string expectedPath)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedPath, route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, 1, "en-US")]
|
||||
[TestCase(1, 1, "da-DK")]
|
||||
[TestCase(2, 2, "en-US")]
|
||||
[TestCase(2, 2, "da-DK")]
|
||||
[TestCase(3, 3, "en-US")]
|
||||
[TestCase(3, 3, "da-DK")]
|
||||
[TestCase(1, 2, "en-US")]
|
||||
[TestCase(1, 2, "da-DK")]
|
||||
[TestCase(2, 3, "en-US")]
|
||||
[TestCase(2, 3, "da-DK")]
|
||||
[TestCase(3, 1, "en-US")]
|
||||
[TestCase(3, 1, "da-DK")]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureOmitTrailingSlash))]
|
||||
public void Child_Without_Trailing_Slash(int root, int child, string culture)
|
||||
{
|
||||
SetVariationContext(culture);
|
||||
var publishedContent = GetPublishedContent(_contentByName[$"Root {root}/Child {child}"].Key);
|
||||
var route = ApiContentRouteBuilder.Build(publishedContent);
|
||||
Assert.IsNotNull(route);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual($"/child-{child}-{culture.ToLowerInvariant()}", route.Path);
|
||||
Assert.AreEqual($"root-{root}-{culture.ToLowerInvariant()}", route.StartItem.Path);
|
||||
Assert.AreEqual(_contentByName[$"Root {root}"].Key, route.StartItem.Id);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user