Skip to content

Commit

Permalink
Added testing for various negative cases (#23481)
Browse files Browse the repository at this point in the history
Part of #22308
  • Loading branch information
ajcvickers committed Nov 26, 2020
1 parent d11d48c commit 2433579
Show file tree
Hide file tree
Showing 24 changed files with 608 additions and 107 deletions.
12 changes: 5 additions & 7 deletions src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
Expand Down Expand Up @@ -572,14 +573,11 @@ public virtual InternalEntityEntry StartTracking(InternalEntityEntry entry)
throw new InvalidOperationException(CoreStrings.WrongStateManager(entityType.DisplayName()));
}

var mapKey = entry.Entity ?? entry;
var existingEntry = TryGetEntry(mapKey, entityType);
#if DEBUG
var existingEntry = TryGetEntry(entry.Entity ?? entry, entityType);

if (existingEntry != null
&& existingEntry != entry)
{
throw new InvalidOperationException(CoreStrings.MultipleEntries(entityType.DisplayName()));
}
Check.DebugAssert(existingEntry == null || existingEntry == entry, "Duplicate InternalEntityEntry");
#endif

foreach (var key in entityType.GetKeys())
{
Expand Down
22 changes: 19 additions & 3 deletions src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,25 @@ public virtual InternalPropertyBuilder HasValueComparer(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual bool CanSetValueComparer([CanBeNull] ValueComparer comparer, ConfigurationSource? configurationSource)
=> (configurationSource.Overrides(Metadata.GetValueComparerConfigurationSource())
&& Metadata.CheckValueComparer(comparer) == null)
|| Metadata[CoreAnnotationNames.ValueComparer] == comparer;
{
if (configurationSource.Overrides(Metadata.GetValueComparerConfigurationSource()))
{
var errorString = Metadata.CheckValueComparer(comparer);
if (errorString != null)
{
if (configurationSource == ConfigurationSource.Explicit)
{
throw new InvalidOperationException(errorString);
}

return false;
}

return true;
}

return Metadata[CoreAnnotationNames.ValueComparer] == comparer;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
22 changes: 0 additions & 22 deletions src/EFCore/Properties/CoreStrings.Designer.cs

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

9 changes: 0 additions & 9 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -968,9 +968,6 @@
<data name="ModelNotFinalized" xml:space="preserve">
<value>The model must be finalized before '{method}' can be used. Ensure that either 'OnModelCreating' has completed or, if using a stand-alone 'ModelBuilder', that 'FinalizeModel' has been called.</value>
</data>
<data name="MultipleEntries" xml:space="preserve">
<value>Cannot start tracking InternalEntityEntry for entity type '{entityType}' because another InternalEntityEntry is already tracking the same entity.</value>
</data>
<data name="MultipleFilteredIncludesOnSameNavigation" xml:space="preserve">
<value>The filters '{filter1}' and '{filter2}' have both been configured on the same included navigation. Only one unique filter per navigation is allowed. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.</value>
</data>
Expand Down Expand Up @@ -1040,9 +1037,6 @@
<data name="NoClrNavigation" xml:space="preserve">
<value>The navigation '{navigation}' cannot be added to the entity type '{entityType}' because there is no corresponding CLR property on the underlying type and navigations properties cannot be added in shadow state.</value>
</data>
<data name="NoClrType" xml:space="preserve">
<value>The CLR entity materializer cannot be used for entity type '{entityType}' because it is a shadow state entity type. Materialization to a CLR type is only possible for entity types that have a corresponding CLR type.</value>
</data>
<data name="NoDefiningNavigation" xml:space="preserve">
<value>The navigation '{navigation}' used to define the entity type '{entityType}' is not present on '{definingEntityType}'.</value>
</data>
Expand Down Expand Up @@ -1227,9 +1221,6 @@
<data name="PropertyWrongType" xml:space="preserve">
<value>The property '{property}' cannot be removed from the entity type '{entityType}' because it is declared on the entity type '{otherEntityType}'.</value>
</data>
<data name="QueryContextAlreadyInitializedStateManager" xml:space="preserve">
<value>The 'InitializeStateManager' method has been called multiple times on the current query context. This method is intended to be called only once before query enumeration starts.</value>
</data>
<data name="QueryEntityMaterializationConditionWrongShape" xml:space="preserve">
<value>The materialization condition passed for entity shaper of entity type '{entityType}' is not of the correct shape. A materialization condition must be a 'LambdaExpression' of 'Func&lt;ValueBuffer, IEntityType&gt;'.</value>
</data>
Expand Down
1 change: 0 additions & 1 deletion src/EFCore/Query/EntityShaperExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

Expand Down
8 changes: 3 additions & 5 deletions src/EFCore/Query/Internal/EntityMaterializerSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

#nullable enable
Expand Down Expand Up @@ -58,14 +59,11 @@ public virtual Expression CreateMaterializeExpression(
string entityInstanceName,
Expression materializationContextExpression)
{
if (!entityType.HasClrType)
{
throw new InvalidOperationException(CoreStrings.NoClrType(entityType.DisplayName()));
}
Check.DebugAssert(entityType.HasClrType, "Cannot materialize shadow types.");

if (entityType.IsAbstract())
{
throw new InvalidOperationException(CoreStrings.CannotMaterializeAbstractType(entityType));
throw new InvalidOperationException(CoreStrings.CannotMaterializeAbstractType(entityType.DisplayName()));
}

var constructorBinding = (InstantiationBinding?)entityType[CoreAnnotationNames.ConstructorBinding];
Expand Down
7 changes: 3 additions & 4 deletions src/EFCore/Query/QueryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,9 @@ public virtual void AddParameter(string name, object? value)
/// <param name="standAlone"> Whether a stand-alone <see cref="IStateManager" /> should be created to perform identity resolution. </param>
public virtual void InitializeStateManager(bool standAlone = false)
{
if (_stateManager != null)
{
throw new InvalidOperationException(CoreStrings.QueryContextAlreadyInitializedStateManager);
}
Check.DebugAssert(
_stateManager == null,
"The 'InitializeStateManager' method has been called multiple times on the current query context. This method is intended to be called only once before query enumeration starts.");

_stateManager = standAlone
? new StateManager(Dependencies.StateManager.Dependencies)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@
using System.Net;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Xunit;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
{
public class CosmosDbContextOptionsExtensionsTests
{
[ConditionalFact]
public void Throws_with_multiple_providers_new_when_no_provider()
{
var options = new DbContextOptionsBuilder()
.UseCosmos("serviceEndPoint", "authKeyOrResourceToken", "databaseName")
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;

var context = new DbContext(options);

Assert.Equal(
CoreStrings.MultipleProvidersConfigured("'Microsoft.EntityFrameworkCore.Cosmos', 'Microsoft.EntityFrameworkCore.InMemory'"),
Assert.Throws<InvalidOperationException>(() => context.Model).Message);
}

[ConditionalFact]
public void Can_create_options_with_specified_region()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
Expand Down Expand Up @@ -166,6 +167,58 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
null);
}

[ConditionalFact]
public void Required_options_to_GenerateModel_are_not_null()
{
var services = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices();
new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
services.AddSingleton<IProviderCodeGeneratorPlugin, TestCodeGeneratorPlugin>();

var generator = services
.BuildServiceProvider()
.GetRequiredService<IModelCodeGenerator>();

Assert.StartsWith(
CoreStrings.ArgumentPropertyNull(nameof(ModelCodeGenerationOptions.ModelNamespace), "options"),
Assert.Throws<ArgumentException>(
() =>
generator.GenerateModel(
new Model(),
new ModelCodeGenerationOptions
{
ModelNamespace = null,
ContextName = "TestDbContext",
ConnectionString = "Initial Catalog=TestDatabase"
})).Message);

Assert.StartsWith(
CoreStrings.ArgumentPropertyNull(nameof(ModelCodeGenerationOptions.ContextName), "options"),
Assert.Throws<ArgumentException>(
() =>
generator.GenerateModel(
new Model(),
new ModelCodeGenerationOptions
{
ModelNamespace = "TestNamespace",
ContextName = null,
ConnectionString = "Initial Catalog=TestDatabase"
})).Message);

Assert.StartsWith(
CoreStrings.ArgumentPropertyNull(nameof(ModelCodeGenerationOptions.ConnectionString), "options"),
Assert.Throws<ArgumentException>(
() =>
generator.GenerateModel(
new Model(),
new ModelCodeGenerationOptions
{
ModelNamespace = "TestNamespace",
ContextName = "TestDbContext",
ConnectionString = null
})).Message);
}

[ConditionalFact]
public void Plugins_work()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public PropertyValuesInMemoryTest(PropertyValuesInMemoryFixture fixture)

public class PropertyValuesInMemoryFixture : PropertyValuesFixtureBase
{
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
=> base.AddOptions(builder).EnableSensitiveDataLogging(false);

protected override ITestStoreFactory TestStoreFactory
=> InMemoryTestStoreFactory.Instance;
}
Expand Down
58 changes: 57 additions & 1 deletion test/EFCore.InMemory.Tests/InMemoryTransactionManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;

Expand All @@ -17,6 +20,59 @@ namespace Microsoft.EntityFrameworkCore
{
public class InMemoryTransactionManagerTest
{
private class FakeTransactionManagerContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ReplaceService<IDbContextTransactionManager, FakeTransactionManager>()
.EnableServiceProviderCaching(false)
.UseInMemoryDatabase(Guid.NewGuid().ToString());
}

private class FakeTransactionManager : IDbContextTransactionManager
{
public void ResetState()
=> throw new NotImplementedException();

public Task ResetStateAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();

public IDbContextTransaction BeginTransaction()
=> throw new NotImplementedException();

public Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();

public void CommitTransaction()
=> throw new NotImplementedException();

public Task CommitTransactionAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();

public void RollbackTransaction()
=> throw new NotImplementedException();

public Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();

public IDbContextTransaction CurrentTransaction
=> throw new NotImplementedException();
}

[ConditionalFact]
public void Enlist_operations_fails_if_provider_does_not_support_enlistment()
{
using var context = new FakeTransactionManagerContext();

Assert.Equal(
CoreStrings.TransactionsNotSupported,
Assert.Throws<NotSupportedException>(() => context.Database.EnlistTransaction(Transaction.Current)).Message);

Assert.Equal(
CoreStrings.TransactionsNotSupported,
Assert.Throws<NotSupportedException>(() => context.Database.GetEnlistedTransaction()).Message);
}

[ConditionalFact]
public void CurrentTransaction_returns_null()
{
Expand Down
Loading

0 comments on commit 2433579

Please sign in to comment.