Working with unit tests and making a few corrections around ContentTypes.
This commit is contained in:
18
src/Umbraco.Tests/CodeFirst/Attributes/MixinAttribute.cs
Normal file
18
src/Umbraco.Tests/CodeFirst/Attributes/MixinAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
|
||||
public class MixinAttribute : Attribute
|
||||
{
|
||||
public MixinAttribute(Type type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Type of the implementing class
|
||||
/// </summary>
|
||||
public Type Type { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ namespace Umbraco.Tests.CodeFirst.Attributes
|
||||
|
||||
if(string.IsNullOrEmpty(PreValue) == false)
|
||||
{
|
||||
Conventions.CreatePrevalueForDataTypeDefinition(definition.DataTypeDefinition.Id, PreValue, 0,
|
||||
string.Empty);
|
||||
//TODO - test inserting a prevalue when a DataTypeDefinition has been created, as its currently throwing a foreignkey constraint error.
|
||||
Conventions.CreatePrevalueForDataTypeDefinition(definition.DataTypeDefinition.Id, PreValue, 0, string.Empty);
|
||||
}
|
||||
|
||||
return definition;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@@ -10,6 +9,7 @@ using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Serialization;
|
||||
using Umbraco.Tests.CodeFirst.Definitions;
|
||||
using Umbraco.Tests.CodeFirst.TestModels;
|
||||
using Umbraco.Tests.CodeFirst.TestModels.Composition;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using umbraco.editorControls.tinyMCE3;
|
||||
@@ -32,7 +32,8 @@ namespace Umbraco.Tests.CodeFirst
|
||||
PluginManager.Current.AssembliesToScan = new[]
|
||||
{
|
||||
typeof(IDataType).Assembly,
|
||||
typeof(tinyMCE3dataType).Assembly
|
||||
typeof(tinyMCE3dataType).Assembly,
|
||||
typeof (ContentTypeBase).Assembly
|
||||
};
|
||||
|
||||
DataTypesResolver.Current = new DataTypesResolver(
|
||||
@@ -187,37 +188,43 @@ namespace Umbraco.Tests.CodeFirst
|
||||
|
||||
}
|
||||
|
||||
private SerializationService SerializationService { get; set; }
|
||||
|
||||
private static int[] GetTopologicalSortOrder(IList<DependencyField> fields)
|
||||
[Test]
|
||||
public void Can_Resolve_ContentType_Composition_And_Save_To_Database()
|
||||
{
|
||||
var g = new TopologicalSorter(fields.Count());
|
||||
var _indexes = new Dictionary<string, int>();
|
||||
ContentTypeDefinitionFactory.ClearContentTypeCache();
|
||||
|
||||
//add vertices
|
||||
for (int i = 0; i < fields.Count(); i++)
|
||||
{
|
||||
_indexes[fields[i].Alias.ToLower()] = g.AddVertex(i);
|
||||
}
|
||||
var metaSeoModel = typeof(MetaSeo);
|
||||
var seoContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(metaSeoModel);
|
||||
var metaModel = typeof(Meta);
|
||||
var metaContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(metaModel);
|
||||
var baseModel = typeof(Base);
|
||||
var baseContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(baseModel);
|
||||
var newsModel = typeof(News);
|
||||
var newsContentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(newsModel);
|
||||
|
||||
//add edges
|
||||
for (int i = 0; i < fields.Count; i++)
|
||||
{
|
||||
if (fields[i].DependsOn != null)
|
||||
{
|
||||
for (int j = 0; j < fields[i].DependsOn.Length; j++)
|
||||
{
|
||||
g.AddEdge(i,
|
||||
_indexes[fields[i].DependsOn[j].ToLower()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int[] result = g.Sort();
|
||||
return result;
|
||||
var mappedContentTypes = ContentTypeDefinitionFactory.RetrieveMappedContentTypes().ToList();
|
||||
ServiceContext.ContentTypeService.Save(mappedContentTypes);
|
||||
|
||||
Assert.That(mappedContentTypes.Count(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Resolve_Full_List_Of_Models_Implementing_ContentTypeBase()
|
||||
{
|
||||
ContentTypeDefinitionFactory.ClearContentTypeCache();
|
||||
|
||||
var foundTypes = PluginManager.Current.ResolveContentTypeBaseTypes();
|
||||
var contentTypeList = foundTypes.Select(ContentTypeDefinitionFactory.GetContentTypeDefinition).ToList();
|
||||
|
||||
var mappedContentTypes = ContentTypeDefinitionFactory.RetrieveMappedContentTypes();
|
||||
|
||||
Assert.That(contentTypeList.Count(), Is.EqualTo(mappedContentTypes.Count()));
|
||||
|
||||
ServiceContext.ContentTypeService.Save(mappedContentTypes);//Save to db
|
||||
}
|
||||
|
||||
private SerializationService SerializationService { get; set; }
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace Umbraco.Tests.CodeFirst.Definitions
|
||||
? PlainPocoConvention(modelType, existing)
|
||||
: ContentTypeConvention(contentTypeAttribute, modelType, existing);
|
||||
|
||||
//Check for interfaces that'll be used for ContentTypeComposition
|
||||
var mixins = GetAliasesFromTypeInterfaces(modelType);
|
||||
|
||||
var definitions = new List<PropertyDefinition>();
|
||||
int order = 0;
|
||||
var objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList();
|
||||
@@ -126,17 +129,48 @@ namespace Umbraco.Tests.CodeFirst.Definitions
|
||||
//Add the resolved ContentType to the internal cache
|
||||
var field = new DependencyField {ContentType = contentType, Alias = contentType.Value.Alias};
|
||||
var dependencies = new List<string>();
|
||||
//If current type has a parent (inherited model) we add the alias of that type as a dependency
|
||||
if(hasParent)
|
||||
{
|
||||
dependencies.Add(parent.Value.Alias);
|
||||
}
|
||||
//Check ContentType for existing 'Allowed ContentTypes'
|
||||
if(contentType.Value.AllowedContentTypes.Any())
|
||||
{
|
||||
dependencies.AddRange(contentType.Value.AllowedContentTypes.Select(allowed => allowed.Alias));
|
||||
}
|
||||
//Check for interfaces with AliasAttribute and add those as dependencies
|
||||
//NOTE: might also be an idea to check if ContentType has already been created/added to cache that implements the interface.
|
||||
if(mixins.Any())
|
||||
{
|
||||
foreach (var mixin in mixins)
|
||||
{
|
||||
if(dependencies.Contains(mixin.Item1)) continue;
|
||||
|
||||
dependencies.Add(mixin.Item1);
|
||||
var isMixinResolved = _contentTypeCache.ContainsKey(mixin.Item2);
|
||||
|
||||
Lazy<IContentType> compositionType = null;
|
||||
|
||||
if (isMixinResolved)
|
||||
{
|
||||
compositionType = _contentTypeCache[mixin.Item2].ContentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetContentTypeDefinition(mixin.Item3);
|
||||
compositionType = _contentTypeCache[mixin.Item2].ContentType;
|
||||
}
|
||||
|
||||
contentType.Value.AddContentType(compositionType.Value);
|
||||
}
|
||||
}
|
||||
field.DependsOn = dependencies.ToArray();
|
||||
_contentTypeCache.AddOrUpdate(modelType.FullName, field, (x, y) => field);
|
||||
return contentType;
|
||||
}
|
||||
|
||||
|
||||
private static int[] GetTopologicalSortOrder(IList<DependencyField> fields)
|
||||
{
|
||||
var g = new TopologicalSorter(fields.Count());
|
||||
@@ -191,6 +225,37 @@ namespace Umbraco.Tests.CodeFirst.Definitions
|
||||
_contentTypeCache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of aliases that correspond to the interfaces on the passed in type,
|
||||
/// which are attributed with the AliasAttribute.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The modelType is also used to ensure that the implementing class doesn't reference
|
||||
/// itself via its interface.
|
||||
/// </remarks>
|
||||
/// <param name="modelType">Type of the model to retrieve interface aliases from</param>
|
||||
/// <returns>An enumerable list of strings with aliases</returns>
|
||||
private static IEnumerable<Tuple<string, string, Type>> GetAliasesFromTypeInterfaces(Type modelType)
|
||||
{
|
||||
var interfaces = modelType.GetInterfaces().ToList().Distinct();
|
||||
var list = new List<Tuple<string, string, Type>>();
|
||||
|
||||
foreach (var interfaceType in interfaces)
|
||||
{
|
||||
var mixinAttribute = interfaceType.FirstAttribute<MixinAttribute>();
|
||||
if (mixinAttribute != null)
|
||||
{
|
||||
if(mixinAttribute.Type == modelType) continue;
|
||||
var contentTypeAttribute = mixinAttribute.Type.FirstAttribute<ContentTypeAttribute>();
|
||||
var contentTypeAlias = contentTypeAttribute == null ? mixinAttribute.Type.Name.ToUmbracoAlias() : contentTypeAttribute.Alias;
|
||||
var tuple = new Tuple<string, string, Type>(contentTypeAlias, mixinAttribute.Type.FullName, mixinAttribute.Type);
|
||||
list.Add(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convention that converts a class decorated with the ContentTypeAttribute to an initial ContentType
|
||||
/// </summary>
|
||||
|
||||
17
src/Umbraco.Tests/CodeFirst/PluginManagerExtensions.cs
Normal file
17
src/Umbraco.Tests/CodeFirst/PluginManagerExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for PluginTypeResolverTests
|
||||
/// </summary>
|
||||
internal static class PluginManagerExtensions
|
||||
{
|
||||
public static IEnumerable<Type> ResolveContentTypeBaseTypes(this PluginManager resolver)
|
||||
{
|
||||
return resolver.ResolveTypes<ContentTypeBase>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
{
|
||||
public class Base : IBase
|
||||
public class Base : ContentTypeBase, IBase
|
||||
{
|
||||
|
||||
[Richtext(PropertyGroup = "Content")]
|
||||
public string BodyContent { get; set; }
|
||||
}
|
||||
|
||||
[Mixin(typeof(Base))]
|
||||
public interface IBase
|
||||
{
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
using umbraco.editorControls.textfield;
|
||||
using umbraco.editorControls.textfieldmultiple;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
{
|
||||
public class Meta : IMeta
|
||||
public class Meta : ContentTypeBase, IMeta
|
||||
{
|
||||
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string MetaKeywords { get; set; }
|
||||
|
||||
[PropertyType(typeof(textfieldMultipleDataType))]
|
||||
public string MetaDescription { get; set; }
|
||||
}
|
||||
|
||||
[Alias("meta", Name = "Meta")]
|
||||
[Mixin(typeof(Meta))]
|
||||
public interface IMeta
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
using umbraco.editorControls.textfieldmultiple;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
{
|
||||
public class MetaSeo : ContentTypeBase, IMetaSeo
|
||||
{
|
||||
[PropertyType(typeof(textfieldMultipleDataType))]
|
||||
public string FriendlySeoStuff { get; set; }
|
||||
}
|
||||
|
||||
[Mixin(typeof(MetaSeo))]
|
||||
public interface IMetaSeo
|
||||
{}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
using umbraco.editorControls.textfield;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
{
|
||||
/// <summary>
|
||||
/// Deriving class is parent, interfaces are compositions
|
||||
/// </summary>
|
||||
public class News : Base, IMeta, ISeo
|
||||
public class News : Base, IMetaSeo, IMeta
|
||||
{
|
||||
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string Author { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels.Composition
|
||||
{
|
||||
public class Seo : ISeo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Alias("seo", Name = "Seo")]
|
||||
public interface ISeo : IBase
|
||||
{}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
[ContentType("modelPage",
|
||||
AllowedChildContentTypes = new[] { typeof(ContentPage) },
|
||||
AllowedTemplates = new[]{"umbMaster"})]
|
||||
public class DecoratedModelPage
|
||||
public class DecoratedModelPage : ContentTypeBase
|
||||
{
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string Author { get; set; }
|
||||
|
||||
@@ -5,7 +5,7 @@ using umbraco.editorControls.textfieldmultiple;
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
[ContentType("home", AllowedChildContentTypes = new[] { typeof(ContentPage) })]
|
||||
public class Home
|
||||
public class Home : ContentTypeBase
|
||||
{
|
||||
[PropertyType(typeof(TextFieldDataType))]
|
||||
public string SiteName { get; set; }
|
||||
|
||||
@@ -4,7 +4,7 @@ using umbraco.editorControls.tinymce;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
public class ModelWithNewDataType
|
||||
public class ModelWithNewDataType : ContentTypeBase
|
||||
{
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string Title { get; set; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
public class NumericModel
|
||||
public class NumericModel : ContentTypeBase
|
||||
{
|
||||
[Numeric("Number DataType", PreValue = "5", PropertyGroup = "Content")]
|
||||
public int Number { get; set; }
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
//Plain Poco Type - plainPocoType
|
||||
public class PlainPocoType
|
||||
//Name: Plain Poco Type - Alias: plainPocoType
|
||||
public class PlainPocoType : ContentTypeBase
|
||||
{
|
||||
//Name: Title, Alias: title, DataType: Text Field
|
||||
public string Title { get; set; }
|
||||
|
||||
//Name: Author, Alias: author, DataType: Text Field
|
||||
public string Author { get; set; }
|
||||
|
||||
//Name: Is Finished, Alias: isFinished, DataType: Yes/No
|
||||
public bool IsFinished { get; set; }
|
||||
|
||||
//Name: Weight, Alias: weight, DataType: Number
|
||||
public int Weight { get; set; }
|
||||
|
||||
//Name: Publish Date, Alias: publishDate, DataType: Datepicker
|
||||
public DateTime PublishDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using umbraco.editorControls.textfield;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
public class SimpleContentPage
|
||||
public class SimpleContentPage : ContentTypeBase
|
||||
{
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string Title { get; set; }
|
||||
|
||||
@@ -3,7 +3,7 @@ using umbraco.editorControls.textfield;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst.TestModels
|
||||
{
|
||||
public class TextPage
|
||||
public class TextPage : ContentTypeBase
|
||||
{
|
||||
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
|
||||
public string Author { get; set; }
|
||||
|
||||
@@ -1,24 +1,82 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.CodeFirst.Attributes;
|
||||
using Umbraco.Tests.CodeFirst.Definitions;
|
||||
using Umbraco.Tests.CodeFirst.TestModels.Composition;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
|
||||
namespace Umbraco.Tests.CodeFirst
|
||||
{
|
||||
[TestFixture]
|
||||
public class TypeInheritanceTest
|
||||
{
|
||||
[SetUp]
|
||||
public void Initialize()
|
||||
{
|
||||
TestHelper.SetupLog4NetForTests();
|
||||
|
||||
//this ensures its reset
|
||||
PluginManager.Current = new PluginManager(false);
|
||||
|
||||
//for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
|
||||
PluginManager.Current.AssembliesToScan = new[]
|
||||
{
|
||||
typeof (ContentTypeBase).Assembly
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Interfaces_From_Type()
|
||||
{
|
||||
var type = typeof (News);
|
||||
var interfaces = type.GetInterfaces().ToList();
|
||||
|
||||
bool hasSeo = interfaces.Any(x => x.Name == typeof(ISeo).Name);
|
||||
bool hasSeo = interfaces.Any(x => x.Name == typeof(IMetaSeo).Name);
|
||||
bool hasMeta = interfaces.Any(x => x.Name == typeof(IMeta).Name);
|
||||
|
||||
Assert.That(hasSeo, Is.True);
|
||||
Assert.That(hasMeta, Is.True);
|
||||
Assert.That(interfaces.Count, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_MixinAttribute_From_Types()
|
||||
{
|
||||
var type = typeof(News);
|
||||
var interfaces = type.GetInterfaces().ToList();
|
||||
|
||||
var list = new List<string>();
|
||||
|
||||
foreach (var interfaceType in interfaces)
|
||||
{
|
||||
var mixinAttribute = interfaceType.FirstAttribute<MixinAttribute>();
|
||||
if(mixinAttribute != null)
|
||||
{
|
||||
var modelType = mixinAttribute.Type;
|
||||
var contentTypeAttribute = modelType.FirstAttribute<ContentTypeAttribute>();
|
||||
var contentTypeAlias = contentTypeAttribute == null ? modelType.Name.ToUmbracoAlias() : contentTypeAttribute.Alias;
|
||||
list.Add(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.That(list.Count, Is.EqualTo(2));
|
||||
Assert.That(list.Any(x => x == "meta"), Is.True);
|
||||
Assert.That(list.Any(x => x == "seo"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Only_One_Type_List_Created()
|
||||
{
|
||||
var foundTypes = PluginManager.Current.ResolveContentTypeBaseTypes();
|
||||
|
||||
Assert.That(foundTypes.Count(), Is.EqualTo(13));
|
||||
Assert.AreEqual(1,
|
||||
PluginManager.Current.GetTypeLists()
|
||||
.Count(x => x.IsTypeList<ContentTypeBase>(PluginManager.TypeResolutionKind.FindAllTypes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user