Commit 32e7452d authored by yangxiaodong's avatar yangxiaodong

Add database connection unit test using ef

parent 0ae97693
[![Build status](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap)
[![Build status](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw/branch/master?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap/branch/master)
# cap # cap
A .net core middleware of eventually consistent in distributed architectures, now developing... A .net core middleware of eventually consistent in distributed architectures, now developing...
......
...@@ -3,44 +3,49 @@ using System.Data.SqlClient; ...@@ -3,44 +3,49 @@ using System.Data.SqlClient;
namespace DotNetCore.CAP.EntityFrameworkCore.Test namespace DotNetCore.CAP.EntityFrameworkCore.Test
{ {
public static class ConnectionUtil public static class ConnectionUtil
{ {
private const string DatabaseVariable = "Cap_SqlServer_DatabaseName"; private const string DatabaseVariable = "Cap_SqlServer_DatabaseName";
private const string ConnectionStringTemplateVariable = "Cap_SqlServer_ConnectionStringTemplate"; private const string ConnectionStringTemplateVariable = "Cap_SqlServer_ConnectionStringTemplate";
private const string MasterDatabaseName = "master"; private const string MasterDatabaseName = "master";
private const string DefaultDatabaseName = @"DotNetCore.CAP.EntityFrameworkCore.Test"; private const string DefaultDatabaseName = @"DotNetCore.CAP.EntityFrameworkCore.Test";
private const string DefaultConnectionStringTemplate = @"Server=.\sqlexpress;Database={0};Trusted_Connection=True;"; //private const string DefaultConnectionStringTemplate = @"Server=.\sqlexpress;Database={0};Trusted_Connection=True;";
private const string DefaultConnectionStringTemplate = @"Server=192.168.2.206;Initial Catalog={0};User Id=sa;Password=123123;MultipleActiveResultSets=True";
public static string GetDatabaseName()
{ public static string GetDatabaseName()
return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName; {
} return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName;
}
public static string GetMasterConnectionString()
{ public static string GetMasterConnectionString()
return string.Format(GetConnectionStringTemplate(), MasterDatabaseName); {
} return string.Format(GetConnectionStringTemplate(), MasterDatabaseName);
}
public static string GetConnectionString()
{ public static string GetConnectionString()
return string.Format(GetConnectionStringTemplate(), GetDatabaseName()); {
} //if (Environment.GetEnvironmentVariable("ASPNETCore_Environment") == "Development")
//{
private static string GetConnectionStringTemplate() // return "Server=192.168.2.206;Initial Catalog=Test2;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True";
{ //}
return return string.Format(GetConnectionStringTemplate(), GetDatabaseName());
Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable) ?? }
DefaultConnectionStringTemplate;
} private static string GetConnectionStringTemplate()
{
public static SqlConnection CreateConnection(string connectionString = null) return
{ Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable) ??
connectionString = connectionString ?? GetConnectionString(); DefaultConnectionStringTemplate;
var connection = new SqlConnection(connectionString); }
connection.Open();
return connection; public static SqlConnection CreateConnection(string connectionString = null)
} {
} connectionString = connectionString ?? GetConnectionString();
var connection = new SqlConnection(connectionString);
connection.Open();
return connection;
}
}
} }
using System.Data; using System.Data;
using System.Threading.Tasks;
using Dapper; using Dapper;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
...@@ -26,10 +27,10 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test ...@@ -26,10 +27,10 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test
{ {
using (CreateScope()) using (CreateScope())
{ {
var context = GetService<CapDbContext>(); var context = GetService<TestDbContext>();
context.Database.EnsureDeleted(); context.Database.EnsureDeleted();
context.Database.Migrate(); context.Database.Migrate();
_sqlObjectInstalled = true; _sqlObjectInstalled = true;
} }
} }
} }
...@@ -38,7 +39,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test ...@@ -38,7 +39,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test
{ {
using (CreateScope()) using (CreateScope())
{ {
var context = GetService<CapDbContext>(); var context = GetService<TestDbContext>();
var commands = new[] var commands = new[]
{ {
......
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public static class DbUtil
{
public static IServiceCollection ConfigureDbServices(string connectionString, IServiceCollection services = null) {
return ConfigureDbServices<CapDbContext>(connectionString, services);
}
public static IServiceCollection ConfigureDbServices<TContext>(string connectionString, IServiceCollection services = null) where TContext : DbContext {
if (services == null) {
services = new ServiceCollection();
}
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<TContext>(options => options.UseSqlServer(connectionString));
return services;
}
public static TContext Create<TContext>(string connectionString) where TContext : DbContext {
var serviceProvider = ConfigureDbServices<TContext>(connectionString).BuildServiceProvider();
return serviceProvider.GetRequiredService<TContext>();
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using DotNetCore.CAP.Infrastructure;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public class DefaultPocoTest : IClassFixture<ScratchDatabaseFixture>
{
private readonly ApplicationBuilder _builder;
public DefaultPocoTest(ScratchDatabaseFixture fixture)
{
var services = new ServiceCollection();
services
.AddDbContext<CapDbContext>(o => o.UseSqlServer(fixture.ConnectionString))
.AddConsistency()
.AddEntityFrameworkStores<CapDbContext>();
services.AddLogging();
var provider = services.BuildServiceProvider();
_builder = new ApplicationBuilder(provider);
using (var scoped = provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var db = scoped.ServiceProvider.GetRequiredService<CapDbContext>())
{
db.Database.EnsureCreated();
}
}
[Fact]
public async Task EnsureStartupUsageWorks()
{
var messageStore = _builder.ApplicationServices.GetRequiredService<ICapMessageStore>();
var messageManager = _builder.ApplicationServices.GetRequiredService<ICapMessageStore>();
Assert.NotNull(messageStore);
Assert.NotNull(messageManager);
var message = new CapSentMessage();
var operateResult = await messageManager.StoreSentMessageAsync(message);
Assert.True(operateResult.Succeeded);
operateResult = await messageManager.RemoveSentMessageAsync(message);
Assert.True(operateResult.Succeeded);
}
}
}
\ No newline at end of file
...@@ -37,10 +37,12 @@ ...@@ -37,10 +37,12 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
<PackageReference Include="System.Data.SqlClient" Version="4.3.1" /> <PackageReference Include="System.Data.SqlClient" Version="4.3.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup> </ItemGroup>
......
using Xunit;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public class EnsuranceTest : DatabaseTestHost
{
[Fact]
public void Ensure()
{
}
}
}
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Test;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
namespace DotNetCore.CAP.EntityFrameworkCore.Test namespace DotNetCore.CAP.EntityFrameworkCore.Test
{ {
public class MessageStoreTest : MessageManagerTestBase, IClassFixture<ScratchDatabaseFixture> public class MessageStoreTest : DatabaseTestHost
{ {
private readonly ScratchDatabaseFixture _fixture;
public MessageStoreTest(ScratchDatabaseFixture fixture)
{
_fixture = fixture;
}
public class ApplicationDbContext : CapDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
[Fact] [Fact]
public void CanCreateSentMessageUsingEF() public void CanCreateSentMessageUsingEF()
{ {
using (var db = CreateContext()) using (var db = CreateContext())
{ {
var guid = Guid.NewGuid().ToString(); var guid = Guid.NewGuid().ToString();
db.CapSentMessages.Add(new CapSentMessage var message = new CapSentMessage
{ {
Id = guid, Id = guid,
Content = "this is message body", Content = "this is message body",
StateName = StateName.Enqueued StateName = StateName.Enqueued
}); };
db.Attach(message).State = Microsoft.EntityFrameworkCore.EntityState.Added;
db.SaveChanges(); db.SaveChanges();
Assert.True(db.CapSentMessages.Any(u => u.Id == guid)); Assert.True(db.CapSentMessages.Any(u => u.Id == guid));
Assert.NotNull(db.CapSentMessages.FirstOrDefault(u => u.StateName == StateName.Enqueued)); Assert.NotNull(db.CapSentMessages.FirstOrDefault(u => u.StateName == StateName.Enqueued));
} }
} }
[Fact] //[Fact]
public async Task CanCreateUsingManager() //public async Task CanCreateUsingManager()
{ //{
var manager = CreateManager(); // var manager = CreateManager();
var guid = Guid.NewGuid().ToString(); // var guid = Guid.NewGuid().ToString();
var message = new CapSentMessage // var message = new CapSentMessage
{ // {
Id = guid, // Id = guid,
Content = "this is message body", // Content = "this is message body",
StateName = StateName.Enqueued, // StateName = StateName.Enqueued,
}; // };
var result = await manager.StoreSentMessageAsync(message); // var result = await manager.StoreSentMessageAsync(message);
Assert.NotNull(result); // Assert.NotNull(result);
Assert.True(result.Succeeded); // Assert.True(result.Succeeded);
result = await manager.RemoveSentMessageAsync(message); // result = await manager.RemoveSentMessageAsync(message);
Assert.NotNull(result); // Assert.NotNull(result);
Assert.True(result.Succeeded); // Assert.True(result.Succeeded);
} //}
public CapDbContext CreateContext(bool delete = false) public TestDbContext CreateContext(bool delete = false)
{ {
var db = DbUtil.Create<CapDbContext>(_fixture.ConnectionString); var db = Provider.GetRequiredService<TestDbContext>();
if (delete) if (delete)
{ {
db.Database.EnsureDeleted(); db.Database.EnsureDeleted();
...@@ -75,31 +60,5 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test ...@@ -75,31 +60,5 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test
db.Database.EnsureCreated(); db.Database.EnsureCreated();
return db; return db;
} }
}
protected override object CreateTestContext()
{
return CreateContext();
}
protected override void AddMessageStore(IServiceCollection services, object context = null)
{
services.AddSingleton<ICapMessageStore>(new CapMessageStore<CapDbContext>((CapDbContext)context));
}
protected override CapSentMessage CreateTestSentMessage(string content = "")
{
return new CapSentMessage
{
Content = content
};
}
protected override CapReceivedMessage CreateTestReceivedMessage(string content = "")
{
return new CapReceivedMessage()
{
Content = content
};
}
}
} }
\ No newline at end of file
...@@ -29,7 +29,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test ...@@ -29,7 +29,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test
var connectionString = ConnectionUtil.GetConnectionString(); var connectionString = ConnectionUtil.GetConnectionString();
//services.AddSingleton(new SqlServerOptions { ConnectionString = connectionString }); //services.AddSingleton(new SqlServerOptions { ConnectionString = connectionString });
services.AddDbContext<CapDbContext>(options => options.UseSqlServer(connectionString)); services.AddDbContext<TestDbContext>(options => options.UseSqlServer(connectionString));
_services = services; _services = services;
} }
......
using System;
using Microsoft.EntityFrameworkCore.Internal;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public class ScratchDatabaseFixture : IDisposable
{
private LazyRef<SqlServerTestStore> _testStore;
public ScratchDatabaseFixture() {
_testStore = new LazyRef<SqlServerTestStore>(() => SqlServerTestStore.CreateScratch());
}
public string ConnectionString => _testStore.Value.Connection.ConnectionString;
public void Dispose() {
if (_testStore.HasValue) {
_testStore.Value?.Dispose();
}
}
}
}
\ No newline at end of file
using System;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public class SqlServerTestStore : IDisposable
{
public const int CommandTimeout = 90;
public static string CreateConnectionString(string name) {
var connStrBuilder = new SqlConnectionStringBuilder(TestEnvironment.Config["Test:SqlServer:DefaultConnectionString"]) {
InitialCatalog = name
};
return connStrBuilder.ConnectionString;
}
public static SqlServerTestStore CreateScratch(bool createDatabase = true)
=> new SqlServerTestStore(GetScratchDbName()).CreateTransient(createDatabase);
private SqlConnection _connection;
private readonly string _name;
private bool _deleteDatabase;
private SqlServerTestStore(string name) {
_name = name;
}
private static string GetScratchDbName() {
string name;
do {
name = "Scratch_" + Guid.NewGuid();
} while (DatabaseExists(name)
|| DatabaseFilesExist(name));
return name;
}
private static void WaitForExists(SqlConnection connection) {
var retryCount = 0;
while (true) {
try {
connection.Open();
connection.Close();
return;
}
catch (SqlException e) {
if (++retryCount >= 30
|| (e.Number != 233 && e.Number != -2 && e.Number != 4060)) {
throw;
}
SqlConnection.ClearPool(connection);
Thread.Sleep(100);
}
}
}
private SqlServerTestStore CreateTransient(bool createDatabase) {
_connection = new SqlConnection(CreateConnectionString(_name));
if (createDatabase) {
using (var master = new SqlConnection(CreateConnectionString("master"))) {
master.Open();
using (var command = master.CreateCommand()) {
command.CommandTimeout = CommandTimeout;
command.CommandText = $"{Environment.NewLine}CREATE DATABASE [{_name}]";
command.ExecuteNonQuery();
WaitForExists(_connection);
}
}
_connection.Open();
}
_deleteDatabase = true;
return this;
}
private static bool DatabaseExists(string name) {
using (var master = new SqlConnection(CreateConnectionString("master"))) {
master.Open();
using (var command = master.CreateCommand()) {
command.CommandTimeout = CommandTimeout;
command.CommandText = $@"SELECT COUNT(*) FROM sys.databases WHERE name = N'{name}'";
return (int)command.ExecuteScalar() > 0;
}
}
}
private static bool DatabaseFilesExist(string name) {
var userFolder = Environment.GetEnvironmentVariable("USERPROFILE") ??
Environment.GetEnvironmentVariable("HOME");
return userFolder != null
&& (File.Exists(Path.Combine(userFolder, name + ".mdf"))
|| File.Exists(Path.Combine(userFolder, name + "_log.ldf")));
}
private void DeleteDatabase(string name) {
using (var master = new SqlConnection(CreateConnectionString("master"))) {
master.Open();
using (var command = master.CreateCommand()) {
command.CommandTimeout = CommandTimeout;
// Query will take a few seconds if (and only if) there are active connections
// SET SINGLE_USER will close any open connections that would prevent the drop
command.CommandText
= string.Format(@"IF EXISTS (SELECT * FROM sys.databases WHERE name = N'{0}')
BEGIN
ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [{0}];
END", name);
command.ExecuteNonQuery();
}
}
}
public DbConnection Connection => _connection;
public void Dispose() {
_connection.Dispose();
if (_deleteDatabase) {
DeleteDatabase(_name);
}
}
}
}
\ No newline at end of file
using System.IO;
using Microsoft.Extensions.Configuration;
namespace DotNetCore.CAP.EntityFrameworkCore.Test
{
public class TestEnvironment
{
public static IConfiguration Config { get; }
static TestEnvironment() {
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: true)
.AddJsonFile("config.test.json", optional: true)
.AddEnvironmentVariables();
Config = configBuilder.Build();
}
}
}
\ No newline at end of file
{
"Test": {
"SqlServer": {
"DefaultConnectionString": "Server=192.168.2.206;Initial Catalog=Test;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment