Merge branch 'contrib' into v15/dev
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
@@ -5,15 +7,34 @@ namespace Umbraco.Cms.Core.Persistence.Repositories;
|
||||
/// </summary>
|
||||
public static class RepositoryCacheKeys
|
||||
{
|
||||
// used to cache keys so we don't keep allocating strings
|
||||
/// <summary>
|
||||
/// A cache for the keys we don't keep allocating strings.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, string> Keys = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repository cache key for the provided type.
|
||||
/// </summary>
|
||||
public static string GetKey<T>()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
return Keys.TryGetValue(type, out var key) ? key : Keys[type] = "uRepo_" + type.Name + "_";
|
||||
|
||||
// The following code is a micro-optimization to avoid an unnecessary lookup in the Keys dictionary, when writing the newly created key.
|
||||
// Previously, the code was:
|
||||
// return Keys.TryGetValue(type, out var key)
|
||||
// ? key
|
||||
// : Keys[type] = "uRepo_" + type.Name + "_";
|
||||
|
||||
// Look up the existing value or get a reference to the newly created default value.
|
||||
ref string? key = ref CollectionsMarshal.GetValueRefOrAddDefault(Keys, type, out _);
|
||||
|
||||
// As we have the reference, we can just assign it if null, without the expensive write back to the dictionary.
|
||||
return key ??= "uRepo_" + type.Name + "_";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repository cache key for the provided type and Id.
|
||||
/// </summary>
|
||||
public static string GetKey<T, TId>(TId? id)
|
||||
{
|
||||
if (EqualityComparer<TId?>.Default.Equals(id, default))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Collections;
|
||||
@@ -7,7 +8,7 @@ using Umbraco.Extensions;
|
||||
namespace Umbraco.Cms.Core.Scoping;
|
||||
|
||||
/// <summary>
|
||||
/// Mechanism for handling read and write locks
|
||||
/// Mechanism for handling read and write locks.
|
||||
/// </summary>
|
||||
public class LockingMechanism : ILockingMechanism
|
||||
{
|
||||
@@ -189,24 +190,43 @@ public class LockingMechanism : ILockingMechanism
|
||||
/// <param name="lockId">Lock ID to increment.</param>
|
||||
/// <param name="instanceId">Instance ID of the scope requesting the lock.</param>
|
||||
/// <param name="locks">Reference to the dictionary to increment on</param>
|
||||
private void IncrementLock(int lockId, Guid instanceId, ref Dictionary<Guid, Dictionary<int, int>>? locks)
|
||||
/// <remarks>Internal for tests.</remarks>
|
||||
internal static void IncrementLock(int lockId, Guid instanceId, ref Dictionary<Guid, Dictionary<int, int>>? locks)
|
||||
{
|
||||
// Since we've already checked that we're the parent in the WriteLockInner method, we don't need to check again.
|
||||
// If it's the very first time a lock has been requested the WriteLocks dict hasn't been instantiated yet.
|
||||
locks ??= new Dictionary<Guid, Dictionary<int, int>>();
|
||||
// If it's the very first time a lock has been requested the WriteLocks dictionary hasn't been instantiated yet.
|
||||
locks ??= [];
|
||||
|
||||
// Try and get the dict associated with the scope id.
|
||||
var locksDictFound = locks.TryGetValue(instanceId, out Dictionary<int, int>? locksDict);
|
||||
// Try and get the dictionary associated with the scope id.
|
||||
|
||||
// The following code is a micro-optimization.
|
||||
// GetValueRefOrAddDefault does lookup or creation with only one hash key generation, internal bucket lookup and value lookup in the bucket.
|
||||
// This compares to doing it twice when initializing, one for the lookup and one for the insertion of the initial value, we had with the
|
||||
// previous code:
|
||||
// var locksDictFound = locks.TryGetValue(instanceId, out Dictionary<int, int>? locksDict);
|
||||
// if (locksDictFound)
|
||||
// {
|
||||
// locksDict!.TryGetValue(lockId, out var value);
|
||||
// locksDict[lockId] = value + 1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // The scope hasn't requested a lock yet, so we have to create a dict for it.
|
||||
// locks.Add(instanceId, new Dictionary<int, int>());
|
||||
// locks[instanceId][lockId] = 1;
|
||||
// }
|
||||
|
||||
ref Dictionary<int, int>? locksDict = ref CollectionsMarshal.GetValueRefOrAddDefault(locks, instanceId, out bool locksDictFound);
|
||||
if (locksDictFound)
|
||||
{
|
||||
locksDict!.TryGetValue(lockId, out var value);
|
||||
locksDict[lockId] = value + 1;
|
||||
// By getting a reference to any existing or default 0 value, we can increment it without the expensive write back into the dictionary.
|
||||
ref int value = ref CollectionsMarshal.GetValueRefOrAddDefault(locksDict!, lockId, out _);
|
||||
value++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The scope hasn't requested a lock yet, so we have to create a dict for it.
|
||||
locks.Add(instanceId, new Dictionary<int, int>());
|
||||
locks[instanceId][lockId] = 1;
|
||||
// The scope hasn't requested a lock yet, so we have to create a dictionary for it.
|
||||
locksDict = new Dictionary<int, int> { { lockId, 1 } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +184,12 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
|
||||
|
||||
foreach (BlockItemData item in items)
|
||||
{
|
||||
// if changes were made to the element type variations, we need those changes reflected in the block property values.
|
||||
// for regular content this happens when a content type is saved (copies of property values are created in the DB),
|
||||
// but for local block level properties we don't have that kind of handling, so we to do it manually.
|
||||
// to be friendly we'll map "formerly invariant properties" to the default language ISO code instead of performing a
|
||||
// hard reset of the property values (which would likely be the most correct thing to do from a data point of view).
|
||||
item.Values = _blockEditorVarianceHandler.AlignPropertyVarianceAsync(item.Values, culture).GetAwaiter().GetResult();
|
||||
foreach (BlockPropertyValue blockPropertyValue in item.Values)
|
||||
{
|
||||
IPropertyType? propertyType = blockPropertyValue.PropertyType;
|
||||
@@ -199,13 +205,6 @@ public abstract class BlockValuePropertyValueEditorBase<TValue, TLayout> : DataV
|
||||
continue;
|
||||
}
|
||||
|
||||
// if changes were made to the element type variation, we need those changes reflected in the block property values.
|
||||
// for regular content this happens when a content type is saved (copies of property values are created in the DB),
|
||||
// but for local block level properties we don't have that kind of handling, so we to do it manually.
|
||||
// to be friendly we'll map "formerly invariant properties" to the default language ISO code instead of performing a
|
||||
// hard reset of the property values (which would likely be the most correct thing to do from a data point of view).
|
||||
_blockEditorVarianceHandler.AlignPropertyVarianceAsync(blockPropertyValue, propertyType, culture).GetAwaiter().GetResult();
|
||||
|
||||
if (!valueEditorsByKey.TryGetValue(propertyType.DataTypeKey, out IDataValueEditor? valueEditor))
|
||||
{
|
||||
var configuration = _dataTypeConfigurationCache.GetConfiguration(propertyType.DataTypeKey);
|
||||
|
||||
@@ -25,15 +25,7 @@ public sealed class BlockEditorVarianceHandler
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns a block property value for variance changes.
|
||||
/// </summary>
|
||||
/// <param name="blockPropertyValue">The block property value to align.</param>
|
||||
/// <param name="propertyType">The underlying property type.</param>
|
||||
/// <param name="culture">The culture being handled (null if invariant).</param>
|
||||
/// <remarks>
|
||||
/// Used for aligning variance changes when editing content.
|
||||
/// </remarks>
|
||||
[Obsolete("Please use the method that allows alignment for a collection of values. Scheduled for removal in V17.")]
|
||||
public async Task AlignPropertyVarianceAsync(BlockPropertyValue blockPropertyValue, IPropertyType propertyType, string? culture)
|
||||
{
|
||||
culture ??= await _languageService.GetDefaultIsoCodeAsync();
|
||||
@@ -45,6 +37,48 @@ public sealed class BlockEditorVarianceHandler
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns a collection of block property values for variance changes.
|
||||
/// </summary>
|
||||
/// <param name="blockPropertyValues">The block property values to align.</param>
|
||||
/// <param name="culture">The culture being handled (null if invariant).</param>
|
||||
/// <remarks>
|
||||
/// Used for aligning variance changes when editing content.
|
||||
/// </remarks>
|
||||
public async Task<IList<BlockPropertyValue>> AlignPropertyVarianceAsync(IList<BlockPropertyValue> blockPropertyValues, string? culture)
|
||||
{
|
||||
var defaultIsoCodeAsync = await _languageService.GetDefaultIsoCodeAsync();
|
||||
culture ??= defaultIsoCodeAsync;
|
||||
|
||||
var valuesToRemove = new List<BlockPropertyValue>();
|
||||
foreach (BlockPropertyValue blockPropertyValue in blockPropertyValues)
|
||||
{
|
||||
IPropertyType? propertyType = blockPropertyValue.PropertyType;
|
||||
if (propertyType is null)
|
||||
{
|
||||
throw new ArgumentException("One or more block properties did not have a resolved property type. Block editor values must be resolved before attempting to map them to editor.", nameof(blockPropertyValues));
|
||||
}
|
||||
|
||||
if (propertyType.VariesByCulture() == VariesByCulture(blockPropertyValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyType.VariesByCulture() is false && blockPropertyValue.Culture.InvariantEquals(defaultIsoCodeAsync) is false)
|
||||
{
|
||||
valuesToRemove.Add(blockPropertyValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPropertyValue.Culture = propertyType.VariesByCulture()
|
||||
? culture
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
return blockPropertyValues.Except(valuesToRemove).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns a block property value for variance changes.
|
||||
/// </summary>
|
||||
@@ -199,6 +233,8 @@ public sealed class BlockEditorVarianceHandler
|
||||
blockValue.Expose.Add(new BlockItemVariation(contentData.Key, value.Culture, value.Segment));
|
||||
}
|
||||
}
|
||||
|
||||
blockValue.Expose = blockValue.Expose.DistinctBy(e => $"{e.ContentKey}.{e.Culture}.{e.Segment}").ToList();
|
||||
}
|
||||
|
||||
private static bool VariesByCulture(BlockPropertyValue blockPropertyValue)
|
||||
|
||||
8
src/Umbraco.Web.UI.Login/package-lock.json
generated
8
src/Umbraco.Web.UI.Login/package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"@umbraco-cms/backoffice": "15.2.1",
|
||||
"msw": "^2.7.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.2",
|
||||
"vite": "^6.2.3",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3772,9 +3772,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
|
||||
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz",
|
||||
"integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"@umbraco-cms/backoffice": "15.2.1",
|
||||
"msw": "^2.7.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.2",
|
||||
"vite": "^6.2.3",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"msw": {
|
||||
|
||||
@@ -185,10 +185,7 @@ public class ContentBuilder
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
if (_cultureNames.TryGetValue(culture, out _))
|
||||
{
|
||||
_cultureNames.Remove(culture);
|
||||
}
|
||||
_cultureNames.Remove(culture);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
@@ -742,6 +741,185 @@ internal partial class BlockListElementLevelVariationTests
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Align_Culture_Variance_For_Variant_Element_Types()
|
||||
{
|
||||
var elementType = CreateElementType(ContentVariation.Culture);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Nothing, blockListDataType);
|
||||
|
||||
var content = CreateContent(
|
||||
contentType,
|
||||
elementType,
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant content value" },
|
||||
new() { Alias = "variantText", Value = "Another invariant content value" }
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant settings value" },
|
||||
new() { Alias = "variantText", Value = "Another invariant settings value" }
|
||||
},
|
||||
false);
|
||||
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ContentTypeService.Save(contentType);
|
||||
|
||||
// re-fetch content
|
||||
content = ContentService.GetById(content.Key);
|
||||
|
||||
var valueEditor = (BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor)blockListDataType.Editor!.GetValueEditor();
|
||||
|
||||
var blockListValue = valueEditor.ToEditor(content!.Properties["blocks"]!) as BlockListValue;
|
||||
Assert.IsNotNull(blockListValue);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.ContentData.Count);
|
||||
Assert.AreEqual(2, blockListValue.ContentData.First().Values.Count);
|
||||
var invariantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.AreEqual("en-US", variantValue.Culture);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.SettingsData.Count);
|
||||
Assert.AreEqual(2, blockListValue.SettingsData.First().Values.Count);
|
||||
var invariantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.AreEqual("en-US", variantValue.Culture);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.Expose.Count);
|
||||
Assert.AreEqual("en-US", blockListValue.Expose.First().Culture);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(ContentVariation.Culture)]
|
||||
[TestCase(ContentVariation.Nothing)]
|
||||
public async Task Can_Turn_Invariant_Element_Variant(ContentVariation contentTypeVariation)
|
||||
{
|
||||
var elementType = CreateElementType(ContentVariation.Nothing);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(contentTypeVariation, blockListDataType);
|
||||
|
||||
var content = CreateContent(
|
||||
contentType,
|
||||
elementType,
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant content value" },
|
||||
new() { Alias = "variantText", Value = "Another invariant content value" }
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant settings value" },
|
||||
new() { Alias = "variantText", Value = "Another invariant settings value" }
|
||||
},
|
||||
false);
|
||||
|
||||
elementType.Variations = ContentVariation.Culture;
|
||||
elementType.PropertyTypes.First(p => p.Alias == "variantText").Variations = ContentVariation.Culture;
|
||||
ContentTypeService.Save(elementType);
|
||||
|
||||
// re-fetch content
|
||||
content = ContentService.GetById(content.Key);
|
||||
|
||||
var valueEditor = (BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor)blockListDataType.Editor!.GetValueEditor();
|
||||
|
||||
var blockListValue = valueEditor.ToEditor(content!.Properties["blocks"]!) as BlockListValue;
|
||||
Assert.IsNotNull(blockListValue);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.ContentData.Count);
|
||||
Assert.AreEqual(2, blockListValue.ContentData.First().Values.Count);
|
||||
var invariantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.AreEqual("en-US", variantValue.Culture);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.SettingsData.Count);
|
||||
Assert.AreEqual(2, blockListValue.SettingsData.First().Values.Count);
|
||||
var invariantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.AreEqual("en-US", variantValue.Culture);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.Expose.Count);
|
||||
Assert.AreEqual("en-US", blockListValue.Expose.First().Culture);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(ContentVariation.Nothing)]
|
||||
[TestCase(ContentVariation.Culture)]
|
||||
public async Task Can_Turn_Variant_Element_Invariant(ContentVariation contentTypeVariation)
|
||||
{
|
||||
var elementType = CreateElementType(ContentVariation.Culture);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(contentTypeVariation, blockListDataType);
|
||||
|
||||
var content = CreateContent(
|
||||
contentType,
|
||||
elementType,
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant content value" },
|
||||
new() { Alias = "variantText", Value = "Variant content in English", Culture = "en-US" },
|
||||
new() { Alias = "variantText", Value = "Variant content in Danish", Culture = "da-DK" }
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "The invariant settings value" },
|
||||
new() { Alias = "variantText", Value = "Variant settings in English", Culture = "en-US" },
|
||||
new() { Alias = "variantText", Value = "Variant settings in Danish", Culture = "da-DK" }
|
||||
},
|
||||
false);
|
||||
|
||||
elementType.Variations = ContentVariation.Nothing;
|
||||
elementType.PropertyTypes.First(p => p.Alias == "variantText").Variations = ContentVariation.Nothing;
|
||||
ContentTypeService.Save(elementType);
|
||||
|
||||
// re-fetch content
|
||||
content = ContentService.GetById(content.Key);
|
||||
|
||||
var valueEditor = (BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor)blockListDataType.Editor!.GetValueEditor();
|
||||
|
||||
var blockListValue = valueEditor.ToEditor(content!.Properties["blocks"]!) as BlockListValue;
|
||||
Assert.IsNotNull(blockListValue);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.ContentData.Count);
|
||||
Assert.AreEqual(2, blockListValue.ContentData.First().Values.Count);
|
||||
var invariantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.ContentData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.IsNull(variantValue.Culture);
|
||||
Assert.AreEqual("Variant content in English", variantValue.Value);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.SettingsData.Count);
|
||||
Assert.AreEqual(2, blockListValue.SettingsData.First().Values.Count);
|
||||
var invariantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "invariantText");
|
||||
var variantValue = blockListValue.SettingsData.First().Values.First(value => value.Alias == "variantText");
|
||||
Assert.IsNull(invariantValue.Culture);
|
||||
Assert.IsNull(variantValue.Culture);
|
||||
Assert.AreEqual("Variant settings in English", variantValue.Value);
|
||||
});
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(1, blockListValue.Expose.Count);
|
||||
Assert.IsNull(blockListValue.Expose.First().Culture);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<IUser> CreateLimitedUser()
|
||||
{
|
||||
var userGroupService = GetRequiredService<IUserGroupService>();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors;
|
||||
|
||||
[TestFixture]
|
||||
public class RepositoryCacheKeysTests
|
||||
{
|
||||
[Test]
|
||||
public void GetKey_Returns_Expected_Key_For_Type()
|
||||
{
|
||||
var key = RepositoryCacheKeys.GetKey<IContent>();
|
||||
Assert.AreEqual("uRepo_IContent_", key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetKey_Returns_Expected_Key_For_Type_And_Id()
|
||||
{
|
||||
var key = RepositoryCacheKeys.GetKey<IContent, int>(1000);
|
||||
Assert.AreEqual("uRepo_IContent_1000", key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping;
|
||||
|
||||
[TestFixture]
|
||||
internal class LockingMechanismTests
|
||||
{
|
||||
private const int LockId = 1000;
|
||||
private const int LockId2 = 1001;
|
||||
private static readonly Guid _scopeInstanceId = Guid.NewGuid();
|
||||
|
||||
[Test]
|
||||
public void IncrementLock_WithoutLocksDictionary_CreatesLock()
|
||||
{
|
||||
var locks = new Dictionary<Guid, Dictionary<int, int>>();
|
||||
LockingMechanism.IncrementLock(LockId, _scopeInstanceId, ref locks);
|
||||
Assert.AreEqual(1, locks.Count);
|
||||
Assert.AreEqual(1, locks[_scopeInstanceId][LockId]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IncrementLock_WithExistingLocksDictionary_CreatesLock()
|
||||
{
|
||||
var locks = new Dictionary<Guid, Dictionary<int, int>>()
|
||||
{
|
||||
{
|
||||
_scopeInstanceId,
|
||||
new Dictionary<int, int>()
|
||||
{
|
||||
{ LockId, 100 },
|
||||
{ LockId2, 200 }
|
||||
}
|
||||
}
|
||||
};
|
||||
LockingMechanism.IncrementLock(LockId, _scopeInstanceId, ref locks);
|
||||
Assert.AreEqual(1, locks.Count);
|
||||
Assert.AreEqual(2, locks[_scopeInstanceId].Count);
|
||||
Assert.AreEqual(101, locks[_scopeInstanceId][LockId]);
|
||||
Assert.AreEqual(200, locks[_scopeInstanceId][LockId2]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user