Skip to content

Commit

Permalink
Add more tests for exceptions (#23159)
Browse files Browse the repository at this point in the history
Part of #22308
  • Loading branch information
AndriySvyryd authored Nov 2, 2020
1 parent f2d01b9 commit 400fbb3
Show file tree
Hide file tree
Showing 26 changed files with 453 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1233,7 +1233,7 @@ private static IProperty FindSharedObjectRootProperty([NotNull] IProperty proper
{
throw new InvalidOperationException(
RelationalStrings.PropertyNotMappedToTable(
property.Name, property.DeclaringEntityType, storeObject.DisplayName()));
property.Name, property.DeclaringEntityType.DisplayName(), storeObject.DisplayName()));
}

var rootProperty = property;
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Metadata/Internal/DbFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ public virtual bool SetIsNullable(bool nullable, ConfigurationSource configurati
{
if (!IsScalar)
{
new InvalidOperationException(RelationalStrings.NonScalarFunctionCannotBeNullable(Name));
throw new InvalidOperationException(RelationalStrings.NonScalarFunctionCannotBeNullable(Name));
}

_nullable = nullable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ public virtual bool SetPropagatesNullability(bool propagatesNullability, Configu
{
if (!Function.IsScalar)
{
new InvalidOperationException(RelationalStrings.NonScalarFunctionParameterCannotPropagatesNullability(Name, Function.Name));
throw new InvalidOperationException(
RelationalStrings.NonScalarFunctionParameterCannotPropagatesNullability(Name, Function.Name));
}

_propagatesNullability = propagatesNullability;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
</resheader>
<data name="BadSequenceString" xml:space="preserve">
<value>Unable to deserialize a sequence from model metadata. See inner exception for details.</value>
<comment>Obsolete</comment>
</data>
<data name="BadSequenceType" xml:space="preserve">
<value>Invalid type for sequence. Valid types are long (the default), int, short, byte and decimal.</value>
Expand Down Expand Up @@ -677,7 +678,7 @@
<value>Unable to translate set operations when both sides don't assign values to the same properties in the nominal type. Please make sure that the same properties are included on both sides, and consider assigning default values if a property doesn't require a specific value.</value>
</data>
<data name="PropertyNotMappedToTable" xml:space="preserve">
<value>The property '{property}' on entity type '{entityType}' is not mapped to the table '{table}'.</value>
<value>The property '{property}' on entity type '{entityType}' is not mapped to '{table}'.</value>
</data>
<data name="ReadonlyEntitySaved" xml:space="preserve">
<value>The entity type '{entityType}' is not mapped to a table, therefore the entities cannot be persisted to the database. Call 'ToTable' in 'OnModelCreating' to map it to a table.</value>
Expand All @@ -689,7 +690,7 @@
<value>Cannot create a 'SelectExpression' with a custom 'TableExpressionBase' since the result type '{entityType}' is part of a hierarchy and does not contain a discriminator property.</value>
</data>
<data name="SetOperationsNotAllowedAfterClientEvaluation" xml:space="preserve">
<value>Unable to translate set operation after client projection has been applied. Consider moving the set operation before the last 'Select' call.</value>
<value>Unable to translate set operation after client projection has been applied. Consider moving the set operation before the last 'Select' call.</value>
</data>
<data name="SetOperationsOnDifferentStoreTypes" xml:space="preserve">
<value>Unable to translate set operation when matching columns on both sides have different store types.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public ImmediateConventionScope([NotNull] ConventionSet conventionSet, Conventio
}

public override void Run(ConventionDispatcher dispatcher)
=> throw new NotImplementedException(CoreStrings.ImmediateConventionScopeCannotBeRunAgain);
=> Check.DebugAssert(false, "Immediate convention scope cannot be run again.");

public IConventionModelBuilder OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder)
{
Expand Down
16 changes: 8 additions & 8 deletions src/EFCore/Metadata/Internal/EntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,14 @@ public virtual IEnumerable<Key> GetDeclaredKeys()
{
Check.NotEmpty(properties, nameof(properties));

var wrongEntityTypeProperty = properties.FirstOrDefault(p => !p.DeclaringEntityType.IsAssignableFrom(this));
if (wrongEntityTypeProperty != null)
{
throw new InvalidOperationException(
CoreStrings.KeyWrongType(
properties.Format(), this.DisplayName(), wrongEntityTypeProperty.DeclaringEntityType.DisplayName()));
}

var key = FindDeclaredKey(properties);
return key == null
? null
Expand Down Expand Up @@ -1698,14 +1706,6 @@ public virtual IEnumerable<Navigation> GetNavigations()
shouldThrow: true);
}

Navigation.IsCompatible(
name,
memberInfo,
this,
targetEntityType,
collection,
shouldThrow: true);

var skipNavigation = new SkipNavigation(
name,
memberInfo as PropertyInfo,
Expand Down
1 change: 1 addition & 0 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@
</data>
<data name="ImmediateConventionScopeCannotBeRunAgain" xml:space="preserve">
<value>Immediate convention scope cannot be run again.</value>
<comment>Obsolete</comment>
</data>
<data name="ImplementationTypeRequired" xml:space="preserve">
<value>The implementation type for the registration of the '{service}' service could not be determined. Specific implementation types must be used for services that expect multiple registrations so as to avoid duplicates.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@ public virtual void Detects_invalid_concurrency_token()
VerifyError(CosmosStrings.NonETagConcurrencyToken(typeof(Customer).Name, "_not_etag"), model);
}

[ConditionalFact]
public virtual void Detects_nonString_concurrency_token()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Customer>()
.ToContainer("Orders")
.Property<int>("_etag")
.IsConcurrencyToken();

var model = modelBuilder.Model;
VerifyError(CosmosStrings.ETagNonStringStoreType("_etag", typeof(Customer).Name, "int"), model);
}

protected override TestHelpers TestHelpers
=> CosmosTestHelpers.Instance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,20 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_stored_setting()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Animal>();
modelBuilder.Entity<Cat>().Property(c => c.Breed).HasColumnName("Breed").HasComputedColumnSql("1", true);
modelBuilder.Entity<Dog>().Property(c => c.Breed).HasColumnName("Breed").HasComputedColumnSql("1");

VerifyError(
RelationalStrings.DuplicateColumnNameIsStoredMismatch(
nameof(Cat), nameof(Cat.Breed), nameof(Dog), nameof(Dog.Breed), nameof(Cat.Breed), nameof(Animal), "True", ""),
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_DefaultValue()
{
Expand Down Expand Up @@ -622,6 +636,48 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_collations()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Animal>();
modelBuilder.Entity<Cat>().Property(c => c.Breed).HasColumnName("Breed").UseCollation("UTF8");
modelBuilder.Entity<Dog>().Property(c => c.Breed).HasColumnName("Breed");

VerifyError(
RelationalStrings.DuplicateColumnNameCollationMismatch(
nameof(Cat), nameof(Cat.Breed), nameof(Dog), nameof(Dog.Breed), nameof(Cat.Breed), nameof(Animal), "UTF8", ""),
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_precision()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Animal>();
modelBuilder.Entity<Cat>().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1);
modelBuilder.Entity<Dog>().Property(c => c.Breed).HasColumnName("Breed");

VerifyError(
RelationalStrings.DuplicateColumnNamePrecisionMismatch(
nameof(Cat), nameof(Cat.Breed), nameof(Dog), nameof(Dog.Breed), nameof(Cat.Breed), nameof(Animal), "", "1"),
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_scale()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Animal>();
modelBuilder.Entity<Cat>().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1, 2);
modelBuilder.Entity<Dog>().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1);

VerifyError(
RelationalStrings.DuplicateColumnNameScaleMismatch(
nameof(Cat), nameof(Cat.Breed), nameof(Dog), nameof(Dog.Breed), nameof(Cat.Breed), nameof(Animal), "", "2"),
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Passes_for_compatible_duplicate_column_names_within_hierarchy()
{
Expand Down Expand Up @@ -660,6 +716,27 @@ public virtual void Passes_for_shared_columns()
Validate(modelBuilder.Model);
}

[ConditionalFact(Skip = "Issue #23144")]
public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_on_different_tables()
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.Entity<Animal>();
modelBuilder.Entity<Cat>().HasOne<Person>().WithMany().HasForeignKey("FriendId").HasConstraintName("FK");
modelBuilder.Entity<Dog>().HasOne<Person>().WithMany().HasForeignKey("FriendId").HasConstraintName("FK");

modelBuilder.Entity<Cat>().ToTable("Cats");
modelBuilder.Entity<Dog>().ToTable("Dogs");

VerifyError(
RelationalStrings.DuplicateForeignKeyTableMismatch(
"{'FriendId'}", nameof(Dog),
"{'FriendId'}", nameof(Cat),
"FK",
"Cats",
"Dogs"),
modelBuilder.Model);
}

[ConditionalFact]
public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_principal_tables()
{
Expand Down
30 changes: 30 additions & 0 deletions test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,36 @@ var queryableNoParams
Assert.Same(entityType, mapping.EntityType);
}

[ConditionalFact]
public void IsNullable_throws_for_nonScalar()
{
var modelBuilder = GetModelBuilder();

var queryableNoParams
= typeof(MyDerivedContext)
.GetRuntimeMethod(nameof(MyDerivedContext.QueryableNoParams), Array.Empty<Type>());

Assert.Equal(
RelationalStrings.NonScalarFunctionCannotBeNullable(nameof(MyDerivedContext.QueryableNoParams)),
Assert.Throws<InvalidOperationException>(() => modelBuilder.HasDbFunction(queryableNoParams).IsNullable()).Message);
}

[ConditionalFact]
public void PropagatesNullability_throws_for_nonScalar()
{
var modelBuilder = GetModelBuilder();

var queryableSingleParam = typeof(MyDerivedContext)
.GetRuntimeMethod(nameof(MyDerivedContext.QueryableSingleParam), new[] { typeof(int) });

var function = modelBuilder.HasDbFunction(queryableSingleParam);
var parameter = function.HasParameter("i");

Assert.Equal(
RelationalStrings.NonScalarFunctionParameterCannotPropagatesNullability("i", nameof(MyDerivedContext.QueryableSingleParam)),
Assert.Throws<InvalidOperationException>(() => parameter.PropagatesNullability()).Message);
}

[ConditionalFact]
public void DbParameters_invalid_parameter_name_throws()
{
Expand Down
19 changes: 19 additions & 0 deletions test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ namespace Microsoft.EntityFrameworkCore.Metadata
{
public class RelationalModelTest
{
[ConditionalFact]
public void GetRelationalModel_throws_if_convention_has_not_run()
{
var modelBuilder = CreateConventionModelBuilder();

Assert.Equal(
RelationalStrings.DatabaseModelMissing,
Assert.Throws<InvalidOperationException>(
() => modelBuilder.Model.GetRelationalModel()).Message);
}

[ConditionalTheory]
[InlineData(true, Mapping.TPH)]
[InlineData(true, Mapping.TPT)]
Expand Down Expand Up @@ -450,6 +461,14 @@ private static void AssertTables(IRelationalModel model, Mapping mapping)
var specialityColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality));
Assert.False(specialityColumn.IsNullable);

var specialityProperty = specialityColumn.PropertyMappings.First().Property;

Assert.Equal(
RelationalStrings.PropertyNotMappedToTable(
nameof(SpecialCustomer.Speciality), nameof(SpecialCustomer), "Customer"),
Assert.Throws<InvalidOperationException>(() =>
specialityProperty.IsColumnNullable(StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))).Message);

Assert.Equal(3, customerPk.GetMappedConstraints().Count());
var specialCustomerPkConstraint = specialCustomerTable.PrimaryKey;
Assert.Equal("PK_SpecialCustomer", specialCustomerPkConstraint.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,14 @@ public void Throws_if_MaxBatchSize_out_of_range()
Assert.Throws<InvalidOperationException>(
() => new FakeRelationalOptionsExtension().WithMaxBatchSize(-1)).Message);
}

[ConditionalFact]
public void Throws_if_MinBatchSize_out_of_range()
{
Assert.Equal(
RelationalStrings.InvalidMinBatchSize(-1),
Assert.Throws<InvalidOperationException>(
() => new FakeRelationalOptionsExtension().WithMinBatchSize(-1)).Message);
}
}
}
24 changes: 16 additions & 8 deletions test/EFCore.Relational.Tests/Update/ModificationCommandTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,27 +349,35 @@ public void ModificationCommand_initialized_correctly_for_deleted_entities_with_
Assert.False(columnMod.IsWrite);
}

[ConditionalFact]
public void ModificationCommand_throws_for_unchanged_entities()
[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public void ModificationCommand_throws_for_unchanged_entities(bool sensitive)
{
var entry = CreateEntry(EntityState.Unchanged);

var command = new ModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, false, null);
var command = new ModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, sensitive, null);

Assert.Equal(
RelationalStrings.ModificationCommandInvalidEntityState("T1", EntityState.Unchanged),
sensitive
? RelationalStrings.ModificationCommandInvalidEntityStateSensitive("T1", "{Id: 1}", EntityState.Unchanged)
: RelationalStrings.ModificationCommandInvalidEntityState("T1", EntityState.Unchanged),
Assert.Throws<InvalidOperationException>(() => command.AddEntry(entry, true)).Message);
}

[ConditionalFact]
public void ModificationCommand_throws_for_unknown_entities()
[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public void ModificationCommand_throws_for_unknown_entities(bool sensitive)
{
var entry = CreateEntry(EntityState.Detached);

var command = new ModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, false, null);
var command = new ModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, sensitive, null);

Assert.Equal(
RelationalStrings.ModificationCommandInvalidEntityState("T1", EntityState.Detached),
sensitive
? RelationalStrings.ModificationCommandInvalidEntityStateSensitive("T1", "{Id: 1}", EntityState.Detached)
: RelationalStrings.ModificationCommandInvalidEntityState("T1", EntityState.Detached),
Assert.Throws<InvalidOperationException>(() => command.AddEntry(entry, true)).Message);
}

Expand Down
11 changes: 11 additions & 0 deletions test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ public virtual Task SelectMany_on_owned_collection(bool async)
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Set_throws_for_owned_type(bool async)
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => AssertQuery(async, ss => ss.Set<Order>()));

Assert.Equal(
CoreStrings.InvalidSetTypeOwned(nameof(Order), nameof(OwnedPerson)),
exception.Message);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Set_throws_for_owned_type_with_defining_navigation(bool async)
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => AssertQuery(async, ss => ss.Set<OwnedAddress>()));

Expand Down
Loading

0 comments on commit 400fbb3

Please sign in to comment.