diff --git a/src/Caching/Caching.slnf b/src/Caching/Caching.slnf new file mode 100644 index 000000000000..2a8045ab9194 --- /dev/null +++ b/src/Caching/Caching.slnf @@ -0,0 +1,41 @@ +{ + "solution": { + "path": "..\\..\\Extensions.sln", + "projects": [ + "src\\Caching\\Abstractions\\src\\Microsoft.Extensions.Caching.Abstractions.csproj", + "src\\Caching\\Memory\\src\\Microsoft.Extensions.Caching.Memory.csproj", + "src\\Caching\\Memory\\test\\Microsoft.Extensions.Caching.Memory.Tests.csproj", + "src\\Caching\\SqlServer\\src\\Microsoft.Extensions.Caching.SqlServer.csproj", + "src\\Caching\\SqlServer\\test\\Microsoft.Extensions.Caching.SqlServer.Tests.csproj", + "src\\Caching\\StackExchangeRedis\\src\\Microsoft.Extensions.Caching.StackExchangeRedis.csproj", + "src\\Caching\\StackExchangeRedis\\test\\Microsoft.Extensions.Caching.StackExchangeRedis.Tests.csproj", + "src\\Caching\\samples\\MemoryCacheConcurencySample\\MemoryCacheConcurencySample.csproj", + "src\\Caching\\samples\\MemoryCacheFileWatchSample\\MemoryCacheFileWatchSample.csproj", + "src\\Caching\\samples\\MemoryCacheSample\\MemoryCacheSample.csproj", + "src\\Caching\\samples\\ProfilingSample\\ProfilingSample.csproj", + "src\\Caching\\samples\\RedisCacheSample\\RedisCacheSample.csproj", + "src\\Caching\\samples\\SqlServerCacheConcurencySample\\SqlServerCacheConcurencySample.csproj", + "src\\Caching\\samples\\SqlServerCacheSample\\SqlServerCacheSample.csproj", + "src\\Configuration\\Config.Abstractions\\src\\Microsoft.Extensions.Configuration.Abstractions.csproj", + "src\\Configuration\\Config.Binder\\src\\Microsoft.Extensions.Configuration.Binder.csproj", + "src\\Configuration\\Config.EnvironmentVariables\\src\\Microsoft.Extensions.Configuration.EnvironmentVariables.csproj", + "src\\Configuration\\Config.FileExtensions\\src\\Microsoft.Extensions.Configuration.FileExtensions.csproj", + "src\\Configuration\\Config.Json\\src\\Microsoft.Extensions.Configuration.Json.csproj", + "src\\Configuration\\Config\\src\\Microsoft.Extensions.Configuration.csproj", + "src\\DependencyInjection\\DI.Abstractions\\src\\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", + "src\\DependencyInjection\\DI\\src\\Microsoft.Extensions.DependencyInjection.csproj", + "src\\FileProviders\\Abstractions\\src\\Microsoft.Extensions.FileProviders.Abstractions.csproj", + "src\\FileProviders\\Physical\\src\\Microsoft.Extensions.FileProviders.Physical.csproj", + "src\\FileSystemGlobbing\\src\\Microsoft.Extensions.FileSystemGlobbing.csproj", + "src\\Logging\\Logging.Abstractions\\src\\Microsoft.Extensions.Logging.Abstractions.csproj", + "src\\Logging\\Logging.Configuration\\src\\Microsoft.Extensions.Logging.Configuration.csproj", + "src\\Logging\\Logging.Console\\src\\Microsoft.Extensions.Logging.Console.csproj", + "src\\Logging\\Logging.Testing\\src\\Microsoft.Extensions.Logging.Testing.csproj", + "src\\Logging\\Logging\\src\\Microsoft.Extensions.Logging.csproj", + "src\\Options\\ConfigurationExtensions\\src\\Microsoft.Extensions.Options.ConfigurationExtensions.csproj", + "src\\Options\\Options\\src\\Microsoft.Extensions.Options.csproj", + "src\\Primitives\\src\\Microsoft.Extensions.Primitives.csproj", + "src\\TestingUtils\\Microsoft.AspNetCore.Testing\\src\\Microsoft.AspNetCore.Testing.csproj" + ] + } +} \ No newline at end of file diff --git a/src/Caching/SqlServer/test/Microsoft.Extensions.Caching.SqlServer.Tests.csproj b/src/Caching/SqlServer/test/Microsoft.Extensions.Caching.SqlServer.Tests.csproj index 025fcef8209e..a8c9b2f7a773 100644 --- a/src/Caching/SqlServer/test/Microsoft.Extensions.Caching.SqlServer.Tests.csproj +++ b/src/Caching/SqlServer/test/Microsoft.Extensions.Caching.SqlServer.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0;net472 @@ -11,6 +11,7 @@ + diff --git a/src/Caching/SqlServer/test/SqlServerCacheWithDatabaseTest.cs b/src/Caching/SqlServer/test/SqlServerCacheWithDatabaseTest.cs index 8303c042b0cd..96fcd4f1ce38 100644 --- a/src/Caching/SqlServer/test/SqlServerCacheWithDatabaseTest.cs +++ b/src/Caching/SqlServer/test/SqlServerCacheWithDatabaseTest.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; @@ -18,12 +17,12 @@ namespace Microsoft.Extensions.Caching.SqlServer { public class SqlServerCacheWithDatabaseTest { - private const string SkipReason = "This requires SQL Server database to be set up"; + // These tests are disabled by default. To run them, run the "run-db-tests.ps1" script private const string ConnectionStringKey = "ConnectionString"; private const string SchemaNameKey = "SchemaName"; private const string TableNameKey = "TableName"; - + private const string EnabledEnvVarName = "SQLCACHETESTS_ENABLED"; private readonly string _tableName; private readonly string _schemaName; private readonly string _connectionString; @@ -35,6 +34,8 @@ public SqlServerCacheWithDatabaseTest() var memoryConfigurationData = new Dictionary { + // When creating a test database, these values must be used in the parameters to 'dotnet sql-cache create'. + // If you have to use other parameters for some reason, make sure to update this! { ConnectionStringKey, @"Server=(localdb)\MSSQLLocalDB;Database=CacheTestDb;Trusted_Connection=True;" }, { SchemaNameKey, "dbo" }, { TableNameKey, "CacheTest" }, @@ -43,7 +44,7 @@ public SqlServerCacheWithDatabaseTest() var configurationBuilder = new ConfigurationBuilder(); configurationBuilder .AddInMemoryCollection(memoryConfigurationData) - .AddEnvironmentVariables(); + .AddEnvironmentVariables(prefix: "SQLCACHETESTS_"); var configuration = configurationBuilder.Build(); _tableName = configuration[TableNameKey]; @@ -51,7 +52,8 @@ public SqlServerCacheWithDatabaseTest() _connectionString = configuration[ConnectionStringKey]; } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task ReturnsNullValue_ForNonExistingCacheItem() { // Arrange @@ -64,7 +66,8 @@ public async Task ReturnsNullValue_ForNonExistingCacheItem() Assert.Null(value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpirationSetInThePast_Throws() { // Arrange @@ -84,7 +87,8 @@ public async Task SetWithAbsoluteExpirationSetInThePast_Throws() Assert.Equal("The absolute expiration value must be in the future.", exception.Message); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_SucceedsFor_KeyEqualToMaximumSize() { // Arrange @@ -111,7 +115,8 @@ await cache.SetAsync( Assert.Null(cacheItemInfo); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_SucceedsFor_NullAbsoluteAndSlidingExpirationTimes() { // Arrange @@ -150,7 +155,8 @@ await AssertGetCacheItemFromDatabaseAsync( Assert.Null(cacheItemInfo); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task UpdatedDefaultSlidingExpiration_SetCacheItem_SucceedsFor_NullAbsoluteAndSlidingExpirationTimes() { // Arrange @@ -190,7 +196,8 @@ await AssertGetCacheItemFromDatabaseAsync( Assert.Null(cacheItemInfo); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_FailsFor_KeyGreaterThanMaximumSize() { // Arrange @@ -211,9 +218,10 @@ await cache.SetAsync( } // Arrange - [Theory(Skip = SkipReason)] + [ConditionalTheory] [InlineData(10, 11)] [InlineData(10, 30)] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpiration_ReturnsNullValue_ForExpiredCacheItem( int slidingExpirationWindow, int accessItemAt) { @@ -236,9 +244,10 @@ await cache.SetAsync( Assert.Null(value); } - [Theory(Skip = SkipReason)] + [ConditionalTheory] [InlineData(5, 15)] [InlineData(10, 20)] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpiration_ExtendsExpirationTime(int accessItemAt, int expected) { // Arrange @@ -264,9 +273,10 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: expectedExpirationTime); } - [Theory(Skip = SkipReason)] + [ConditionalTheory] [InlineData(8)] [InlineData(50)] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpirationAndAbsoluteExpiration_ReturnsNullValue_ForExpiredCacheItem( int accessItemAt) { @@ -294,7 +304,8 @@ await cache.SetAsync( Assert.Null(value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpirationRelativeToNow_ReturnsNullValue_ForExpiredCacheItem() { // Arrange @@ -316,7 +327,8 @@ await cache.SetAsync( Assert.Null(value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpiration_ReturnsNullValue_ForExpiredCacheItem() { // Arrange @@ -339,7 +351,8 @@ await cache.SetAsync( Assert.Null(value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoesNotThrowException_WhenOnlyAbsoluteExpirationSupplied_AbsoluteExpirationRelativeToNow() { // Arrange @@ -367,7 +380,8 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: expectedAbsoluteExpiration); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoesNotThrowException_WhenOnlyAbsoluteExpirationSupplied_AbsoluteExpiration() { // Arrange @@ -394,7 +408,8 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: expectedAbsoluteExpiration); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_UpdatesAbsoluteExpirationTime() { // Arrange @@ -433,7 +448,8 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: absoluteExpiration); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_WithValueLargerThan_DefaultColumnWidth() { // Arrange @@ -460,7 +476,8 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: absoluteExpiration); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task ExtendsExpirationTime_ForSlidingExpiration() { // Arrange @@ -489,7 +506,8 @@ await cache.SetAsync( Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetItem_SlidingExpirationDoesNot_ExceedAbsoluteExpirationIfSet() { // Arrange @@ -545,7 +563,8 @@ await AssertGetCacheItemFromDatabaseAsync( expectedExpirationTime: absoluteExpiration); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoestNotExtendsExpirationTime_ForAbsoluteExpiration() { // Arrange @@ -574,7 +593,8 @@ await cache.SetAsync( Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task RefreshItem_ExtendsExpirationTime_ForSlidingExpiration() { // Arrange @@ -603,7 +623,8 @@ await cache.SetAsync( Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetCacheItem_IsCaseSensitive() { // Arrange @@ -621,7 +642,8 @@ await cache.SetAsync( Assert.Null(value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetCacheItem_DoesNotTrimTrailingSpaces() { // Arrange @@ -641,7 +663,8 @@ await cache.SetAsync( Assert.Equal(expectedValue, value); } - [Fact(Skip = SkipReason)] + [ConditionalFact] + [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DeletesCacheItem_OnExplicitlyCalled() { // Arrange diff --git a/src/Caching/SqlServer/test/run-db-tests.ps1 b/src/Caching/SqlServer/test/run-db-tests.ps1 new file mode 100644 index 000000000000..1fc0fb663ce0 --- /dev/null +++ b/src/Caching/SqlServer/test/run-db-tests.ps1 @@ -0,0 +1,98 @@ +param( + [Parameter(Mandatory = $false)][string]$ConnectionString = "Server=(localdb)\MSSQLLocalDB;Database=CacheTestDb;Trusted_Connection=True;", + [Parameter(Mandatory = $false)][string]$SchemaName = "dbo", + [Parameter(Mandatory = $false)][string]$TableName = "CacheTest") + +function ExecuteScalar($Connection, $Script) { + $cmd = New-Object System.Data.SqlClient.SqlCommand + $cmd.Connection = $Connection + $cmd.CommandText = $Script + $cmd.ExecuteScalar() +} + +# Check if the database exists +Write-Host "Checking for database..." +$ServerConnectionBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder $ConnectionString + +if (!$ServerConnectionBuilder.InitialCatalog) { + throw "An 'Initial Catalog' or 'Database' value must be provided in the connection string!" +} +$DatabaseName = $ServerConnectionBuilder.InitialCatalog + +if (!$ServerConnectionBuilder.DataSource -eq "(localdb)\MSSQLLocalDB") { + Write-Warning "This script is really only designed for running against your local instance of SQL Local DB. Continue at your own risk!" +} + +$ServerConnectionBuilder.Remove("Initial Catalog") | Out-Null; +$ServerConnection = New-Object System.Data.SqlClient.SqlConnection $ServerConnectionBuilder.ConnectionString +$ServerConnection.Open(); + +# Yes, this is SQL Injectable, but you're using it on your local machine with the intent of connecting to your local db. +$dbid = ExecuteScalar $ServerConnection "SELECT database_id FROM sys.databases WHERE Name = '$DatabaseName'" +if (!$dbid) { + Write-Host "Database not found, creating..." + + # Create the database + ExecuteScalar $ServerConnection "CREATE DATABASE $DatabaseName" +} + +# Close the server connection +$ServerConnection.Close() + +# Check for the table +$DbConnection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString +$DbConnection.Open(); +$tableid = ExecuteScalar $DbConnection "SELECT object_id FROM sys.objects WHERE type = 'U' AND name = '$TableName'" +if ($tableid) { + Write-Host "Table exists, dropping it..." + ExecuteScalar $DbConnection "DROP TABLE $TableName" +} + +$DbConnection.Close() + +# Fill the database with sql cache goodies +dotnet sql-cache create $ConnectionString $SchemaName $TableName + +# Set environment variables and launch tests +$oldConnectionString = $env:SQLCACHETESTS_ConnectionString +$oldSchemaName = $env:SQLCACHETESTS_SchemaName +$oldTableName = $env:SQLCACHETESTS_TableName +$oldEnabled = $env:SQLCACHETESTS_ENABLED +try { + $env:SQLCACHETESTS_ConnectionString = $ConnectionString + $env:SQLCACHETESTS_SchemaName = $SchemaName + $env:SQLCACHETESTS_TableName = $TableName + $env:SQLCACHETESTS_ENABLED = "1" + + Write-Host "Launching Tests..." + dotnet test "$PSScriptRoot/Microsoft.Extensions.Caching.SqlServer.Tests.csproj" +} +finally { + if ($oldConnectionString) { + $env:SQLCACHETESTS_ConnectionString = $oldConnectionString + } + else { + Remove-Item env:\SQLCACHETESTS_ConnectionString + } + + if ($oldSchemaName) { + $env:SQLCACHETESTS_SchemaName = $oldSchemaName + } + else { + Remove-Item env:\SQLCACHETESTS_SchemaName + } + + if ($oldTableName) { + $env:SQLCACHETESTS_TableName = $oldTableName + } + else { + Remove-Item env:\SQLCACHETESTS_TableName + } + + if ($oldEnabled) { + $env:SQLCACHETESTS_ENABLED = $oldEnabled + } + else { + Remove-Item env:\SQLCACHETESTS_ENABLED + } +}