Skip to content

Commit 0cecfe2

Browse files
committed
Adding unit tests for All Schema rules along with some fake data objects
1 parent e20927a commit 0cecfe2

10 files changed

+422
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
2+
3+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests;
4+
public abstract class BaseMapperTests<TMapper, TSource, TDestination> where TMapper : IMapper<TSource, TDestination>
5+
{
6+
7+
private object[] _additionalParameters;
8+
protected BaseMapperTests(object[] additionalParameters = null)
9+
{
10+
_additionalParameters = additionalParameters ?? Array.Empty<object>();
11+
}
12+
private IMapper<TSource, TDestination> _mapper => CreateMapper();
13+
private TMapper CreateMapper() => (TMapper)Activator.CreateInstance(typeof(TMapper), _additionalParameters);
14+
15+
protected TDestination RunTest(TSource source) => _mapper.Map(source);
16+
17+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.Xrm.Sdk.Metadata;
2+
3+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.FakeBuilders;
4+
internal class FakeEntityMetadataBuilder
5+
{
6+
private string EntityName { get; }
7+
private string PrimaryIdField { get; }
8+
private string PrimaryNameField { get; }
9+
private List<AttributeMetadata> Fields { get; } = new List<AttributeMetadata>();
10+
private List<ManyToManyRelationshipMetadata> ManyToManyRelationships { get; } = new List<ManyToManyRelationshipMetadata>();
11+
12+
public FakeEntityMetadataBuilder(string entityName, string primaryIdField, string primaryNameField)
13+
{
14+
EntityName = entityName;
15+
PrimaryIdField = primaryIdField;
16+
PrimaryNameField = primaryNameField;
17+
}
18+
public FakeEntityMetadataBuilder AddAttribute<T>(string logicalName)
19+
where T : AttributeMetadata, new()
20+
{
21+
var attribute = new T
22+
{
23+
LogicalName = logicalName
24+
};
25+
Fields.Add(attribute);
26+
return this;
27+
}
28+
public FakeEntityMetadataBuilder AddRelationship(string SchemaName, string TargetEntity)
29+
{
30+
var relationship = new ManyToManyRelationshipMetadata
31+
{
32+
Entity1LogicalName = EntityName,
33+
Entity2LogicalName = TargetEntity,
34+
35+
SchemaName = SchemaName
36+
};
37+
ManyToManyRelationships.Add(relationship);
38+
return this;
39+
}
40+
41+
public EntityMetadata Build()
42+
{
43+
44+
var entityMd = new EntityMetadata()
45+
{
46+
LogicalName = EntityName
47+
};
48+
typeof(EntityMetadata).GetProperty(nameof(EntityMetadata.PrimaryIdAttribute))!
49+
.SetValue(entityMd, PrimaryIdField);
50+
typeof(EntityMetadata).GetProperty(nameof(EntityMetadata.PrimaryNameAttribute))!
51+
.SetValue(entityMd, PrimaryNameField);
52+
typeof(EntityMetadata).GetProperty(nameof(EntityMetadata.Attributes))!
53+
.SetValue(entityMd, Fields.ToArray());
54+
typeof(EntityMetadata).GetProperty(nameof(EntityMetadata.ManyToManyRelationships))!
55+
.SetValue(entityMd, ManyToManyRelationships.ToArray());
56+
return entityMd;
57+
}
58+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Tests.FakeBuilders;
2+
using Microsoft.Xrm.Sdk.Metadata;
3+
4+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests;
5+
internal static class FakeMetadata
6+
{
7+
public static EntityMetadata Contact =>
8+
new FakeEntityMetadataBuilder("contact", "contactid", "fullname")
9+
.AddAttribute<StringAttributeMetadata>("firstname")
10+
.AddAttribute<StringAttributeMetadata>("lastname")
11+
.AddRelationship("contact_opportunities", "opportunity")
12+
.Build();
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Mappers;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
3+
using Microsoft.Xrm.Sdk.Metadata;
4+
using Shouldly;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Mappers;
7+
public class FieldSchemaToAttributeTypeMapperTests :
8+
BaseMapperTests<FieldSchemaToAttributeTypeMapper, FieldSchema, AttributeTypeCode?>
9+
{
10+
public static readonly TheoryData<FieldSchema, AttributeTypeCode> TestData = new() {
11+
{ new FieldSchema { Type = "string" }, AttributeTypeCode.String },
12+
{ new FieldSchema { Type = "guid" }, AttributeTypeCode.Uniqueidentifier },
13+
{ new FieldSchema { Type = "entityreference", LookupType = "account|contact" }, AttributeTypeCode.Customer },
14+
{ new FieldSchema { Type = "entityreference", LookupType = "account" }, AttributeTypeCode.Lookup },
15+
{ new FieldSchema { Type = "owner" }, AttributeTypeCode.Owner },
16+
{ new FieldSchema { Type = "state" }, AttributeTypeCode.State },
17+
{ new FieldSchema { Type = "status" }, AttributeTypeCode.Status },
18+
{ new FieldSchema { Type = "decimal" }, AttributeTypeCode.Decimal },
19+
{ new FieldSchema { Type = "optionsetvalue" }, AttributeTypeCode.Picklist },
20+
{ new FieldSchema { Type = "number" }, AttributeTypeCode.Integer },
21+
{ new FieldSchema { Type = "bigint" }, AttributeTypeCode.BigInt },
22+
{ new FieldSchema { Type = "float" }, AttributeTypeCode.Double },
23+
{ new FieldSchema { Type = "bool" }, AttributeTypeCode.Boolean },
24+
{ new FieldSchema { Type = "datetime" }, AttributeTypeCode.DateTime },
25+
{ new FieldSchema { Type = "money" }, AttributeTypeCode.Money }
26+
};
27+
[Theory]
28+
[MemberData(nameof(TestData))]
29+
public void GivenAFieldSchema_WhenItIsMapped_ThenItShouldReturnProperAttributeTypeCode(FieldSchema source, AttributeTypeCode expected)
30+
{
31+
// Act
32+
var result = RunTest(source);
33+
// Assert
34+
result.ShouldBe(expected);
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules;
3+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas;
4+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
5+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas;
6+
using Dataverse.ConfigurationMigrationTool.Console.Features.Shared;
7+
using Microsoft.Xrm.Sdk.Metadata;
8+
using NSubstitute;
9+
using Shouldly;
10+
11+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas;
12+
public class EntitySchemaValidatorTests
13+
{
14+
private readonly IMetadataService metadataService = Substitute.For<IMetadataService>();
15+
[Fact]
16+
public async Task GivenAValidEntitySchema_WhenItIsValidated_ThenItShouldReturnSuccess()
17+
{
18+
// Arrange
19+
var contactMd = FakeMetadata.Contact;
20+
metadataService.GetEntity("contact").Returns(contactMd);
21+
metadataService.GetRelationShipM2M(FakeSchemas.Contact.Relationships.Relationship.First().Name)
22+
.Returns(contactMd.ManyToManyRelationships.FirstOrDefault());
23+
var validFieldSchemaRule = Substitute.For<IFieldSchemaValidationRule>();
24+
validFieldSchemaRule.Validate(Arg.Any<FieldSchema>(), Arg.Any<AttributeMetadata>())
25+
.Returns(RuleResult.Success());
26+
27+
var validRelationshipSchemaRule = Substitute.For<IRelationshipSchemaValidationRule>();
28+
validRelationshipSchemaRule.Validate(Arg.Any<string>(), Arg.Any<RelationshipSchema>(), Arg.Any<ManyToManyRelationshipMetadata>())
29+
.Returns(RuleResult.Success());
30+
var validator = CreateValidator([validFieldSchemaRule], [validRelationshipSchemaRule]);
31+
//Act
32+
var result = await validator.Validate(FakeSchemas.Contact);
33+
//Assert
34+
result.IsError.ShouldBeFalse();
35+
await validFieldSchemaRule.Received(2).Validate(Arg.Any<FieldSchema>(), Arg.Any<AttributeMetadata>());
36+
await validRelationshipSchemaRule.Received(1).Validate(Arg.Any<string>(), Arg.Any<RelationshipSchema>(), Arg.Any<ManyToManyRelationshipMetadata>());
37+
}
38+
39+
40+
41+
42+
43+
private IValidator<EntitySchema> CreateValidator(
44+
IEnumerable<IFieldSchemaValidationRule> fieldSchemaRules,
45+
IEnumerable<IRelationshipSchemaValidationRule> relationshipSchemaRules)
46+
{
47+
return new EntitySchemaValidator(metadataService,
48+
fieldSchemaRules ?? Enumerable.Empty<IFieldSchemaValidationRule>(),
49+
relationshipSchemaRules ?? Enumerable.Empty<IRelationshipSchemaValidationRule>());
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules;
3+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
4+
using Microsoft.Xrm.Sdk.Metadata;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
7+
public abstract class BaseFieldSchemaValidationRuleTests<TRule> where TRule : IFieldSchemaValidationRule
8+
{
9+
protected TMetadata CreateAttributeMetadata<TMetadata>() where TMetadata : AttributeMetadata
10+
{
11+
var md = Activator.CreateInstance<TMetadata>();
12+
return md;
13+
}
14+
protected Task<RuleResult> ExecuteRule(FieldSchema fieldSchema, AttributeMetadata attributeMetadata)
15+
{
16+
var rule = Activator.CreateInstance<TRule>();
17+
return rule.Validate(fieldSchema, attributeMetadata);
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
3+
using Microsoft.Xrm.Sdk.Metadata;
4+
using Shouldly;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
7+
public class FieldTypeMustMatchWithAttributeValidationRuleTests : BaseFieldSchemaValidationRuleTests<FieldTypeMustMatchWithAttributeValidationRule>
8+
{
9+
10+
[Fact]
11+
public async Task GivenAFieldSchemaWithAttributeTypeCode_WhenItIsValidated_ThenItShouldReturnSuccess()
12+
{
13+
// Arrange
14+
var fieldSchema = new FieldSchema
15+
{
16+
Name = "testField",
17+
Type = "string"
18+
};
19+
var attributeMetadata = CreateAttributeMetadata<StringAttributeMetadata>();
20+
// Act
21+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
22+
// Assert
23+
result.IsSuccess.ShouldBe(true);
24+
}
25+
[Fact]
26+
public async Task GivenAnUnresolvedFieldSchemaWithAttributeTypeCode_WhenItIsValidated_ThenItShouldReturnTheProperFailure()
27+
{
28+
var fieldSchema = new FieldSchema
29+
{
30+
Name = "testField",
31+
Type = "string2"
32+
};
33+
var attributeMetadata = CreateAttributeMetadata<StringAttributeMetadata>();
34+
// Act
35+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
36+
// Assert
37+
result.IsSuccess.ShouldBe(false);
38+
result.ErrorMessage.ShouldBe($"Schema Field type {fieldSchema.Type} is not currently supported.");
39+
}
40+
[Fact]
41+
public async Task GivenAFieldSchemaWithWrongAttributeTypeCode_WhenItIsValidated_ThenItShouldReturnTheProperFailure()
42+
{
43+
var fieldSchema = new FieldSchema
44+
{
45+
Name = "testField",
46+
Type = "string"
47+
};
48+
var attributeMetadata = CreateAttributeMetadata<MoneyAttributeMetadata>();
49+
// Act
50+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
51+
// Assert
52+
result.IsSuccess.ShouldBe(false);
53+
result.ErrorMessage.ShouldBe($"Attribute {fieldSchema.Name} is type of String but it's expected to be {attributeMetadata.AttributeType}");
54+
}
55+
}
56+
57+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
3+
using Microsoft.Xrm.Sdk.Metadata;
4+
using Shouldly;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.FieldSchemas;
7+
public class LookupFieldsTargetsMustMatchValidationRuleTests :
8+
BaseFieldSchemaValidationRuleTests<LookupFieldsTargetsMustMatchValidationRule>
9+
{
10+
[Fact]
11+
public async Task GivenANonLookupSchema_WhenItIsValidated_ThenItShouldReturnSuccess()
12+
{
13+
// Arrange
14+
var fieldSchema = new FieldSchema
15+
{
16+
Name = "testField",
17+
Type = "string"
18+
};
19+
var attributeMetadata = CreateAttributeMetadata<StringAttributeMetadata>();
20+
21+
// Act
22+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
23+
24+
// Assert
25+
result.IsSuccess.ShouldBe(true);
26+
}
27+
[Fact]
28+
public async Task GivenAlookupSchemaThatMatchesAttributeMetadataTargets_WhenItIsValidated_ThenItShouldReturnSuccess()
29+
{
30+
// Arrange
31+
var fieldSchema = new FieldSchema
32+
{
33+
Name = "testField",
34+
Type = "entityreference",
35+
LookupType = "account|contact"
36+
};
37+
var attributeMetadata = CreateAttributeMetadata<LookupAttributeMetadata>();
38+
attributeMetadata.Targets = new[] { "account", "contact" };
39+
40+
// Act
41+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
42+
43+
// Assert
44+
result.IsSuccess.ShouldBe(true);
45+
}
46+
[Fact]
47+
public async Task GivenAlookupSchemaThatDoesNotMatchesAttributeMetadataTargets_WhenItIsValidated_ThenItShouldReturnProperFailure()
48+
{
49+
// Arrange
50+
var fieldSchema = new FieldSchema
51+
{
52+
Name = "testField",
53+
Type = "entityreference",
54+
LookupType = "contact"
55+
};
56+
var attributeMetadata = CreateAttributeMetadata<LookupAttributeMetadata>();
57+
attributeMetadata.Targets = new[] { "account", "contact" };
58+
59+
// Act
60+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
61+
62+
// Assert
63+
result.IsSuccess.ShouldBeFalse();
64+
result.ErrorMessage.ShouldBe($"LookupAttribute {fieldSchema.Name} targets contact but it's expected to target: account|contact");
65+
}
66+
[Fact]
67+
public async Task GivenAlookupSchemaWithAnAttributeCodeThatIsOwnerType_WhenItIsValidated_ThenItShouldReturnSuccess()
68+
{
69+
// Arrange
70+
var fieldSchema = new FieldSchema
71+
{
72+
Name = "testField",
73+
Type = "entityreference",
74+
LookupType = "owner"
75+
};
76+
var attributeMetadata = CreateAttributeMetadata<LookupAttributeMetadata>();
77+
typeof(LookupAttributeMetadata).GetProperty(nameof(LookupAttributeMetadata.AttributeType))!
78+
.SetValue(attributeMetadata, AttributeTypeCode.Owner);
79+
attributeMetadata.Targets = new[] { "systemuser", "team" };
80+
81+
// Act
82+
var result = await ExecuteRule(fieldSchema, attributeMetadata);
83+
84+
// Assert
85+
result.IsSuccess.ShouldBeTrue();
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Model;
2+
using Dataverse.ConfigurationMigrationTool.Console.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas;
3+
using Microsoft.Xrm.Sdk.Metadata;
4+
using Shouldly;
5+
6+
namespace Dataverse.ConfigurationMigrationTool.Console.Tests.Features.Import.Validators.Rules.EntitySchemas.RelationshipSchemas;
7+
public class SourceEntityNameMustMatchValidationRuleTests
8+
{
9+
private readonly SourceEntityNameMustMatchValidationRule _rule = new();
10+
11+
[Fact]
12+
public async Task GivenAMatchingSourceEntityName_WhenValidatingRelationshipSchema_ThenItShouldReturnSuccess()
13+
{
14+
// Arrange
15+
var sourceEntityName = "account";
16+
var relationshipSchema = new RelationshipSchema { Name = "account_contact" };
17+
var relationshipMetadata = new ManyToManyRelationshipMetadata
18+
{
19+
Entity1LogicalName = "account"
20+
};
21+
// Act
22+
var result = await _rule.Validate(sourceEntityName, relationshipSchema, relationshipMetadata);
23+
// Assert
24+
result.IsSuccess.ShouldBeTrue();
25+
}
26+
[Fact]
27+
public async Task GivenANonMatchingSourceEntityName_WhenValidatingRelationshipSchema_ThenItShouldReturnProperFailure()
28+
{
29+
// Arrange
30+
var sourceEntityName = "account";
31+
var relationshipSchema = new RelationshipSchema { Name = "account_contact" };
32+
var relationshipMetadata = new ManyToManyRelationshipMetadata
33+
{
34+
Entity1LogicalName = "account1"
35+
};
36+
// Act
37+
var result = await _rule.Validate(sourceEntityName, relationshipSchema, relationshipMetadata);
38+
// Assert
39+
result.IsSuccess.ShouldBeFalse();
40+
result.ErrorMessage.ShouldBe($"ManyToMany Relationship Table {relationshipSchema.Name} Source Entity is {sourceEntityName} but it's expected to be {relationshipMetadata.Entity1LogicalName}");
41+
}
42+
}

0 commit comments

Comments
 (0)