Commit bfad1b3a authored by yangxiaodong's avatar yangxiaodong

Add test cast

parent d949cdae
...@@ -18,7 +18,9 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Cap.Consistency.Test", "tes ...@@ -18,7 +18,9 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Cap.Consistency.Test", "tes
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
test\Shared\MessageManagerTestBase.cs = test\Shared\MessageManagerTestBase.cs
test\Shared\TestConsistencyMessage.cs = test\Shared\TestConsistencyMessage.cs test\Shared\TestConsistencyMessage.cs = test\Shared\TestConsistencyMessage.cs
test\Shared\TestLogger.cs = test\Shared\TestLogger.cs
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Cap.Consistency.Server", "src\Cap.Consistency.Server\Cap.Consistency.Server.xproj", "{55CF2C48-D390-40CF-8AD9-FA39F90E9217}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Cap.Consistency.Server", "src\Cap.Consistency.Server\Cap.Consistency.Server.xproj", "{55CF2C48-D390-40CF-8AD9-FA39F90E9217}"
......
using System;
using Microsoft.EntityFrameworkCore;
namespace Cap.Consistency.EntityFrameworkCore
{
/// <summary>
/// Base class for the Entity Framework database context used for consistency.
/// </summary>
public class ConsistencyDbContext : ConsistencyDbContext<ConsistencyMessage, string>
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsistencyDbContext"/>.
/// </summary>
public ConsistencyDbContext() { }
/// <summary>
/// Initializes a new instance of the <see cref="ConsistencyDbContext"/>.
/// </summary>
/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
public ConsistencyDbContext(DbContextOptions options) : base(options) { }
}
/// <summary>
/// Base class for the Entity Framework database context used for consistency.
/// </summary>
/// <typeparam name="TMessage">The type of message objects.</typeparam>
/// <typeparam name="Tkey">The type of the primarky key for messages.</typeparam>
public abstract class ConsistencyDbContext<TMessage, Tkey> : DbContext
where TMessage : ConsistencyMessage<Tkey>
where Tkey : IEquatable<Tkey>
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsistencyDbContext"/>.
/// </summary>
public ConsistencyDbContext() { }
/// <summary>
/// Initializes a new instance of the <see cref="ConsistencyDbContext"/>.
/// </summary>
/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
public ConsistencyDbContext(DbContextOptions options) : base(options) { }
/// <summary>
/// Gets or sets the <see cref="DbSet{TMessage}"/> of Messages.
/// </summary>
public DbSet<TMessage> Messages { get; set; }
/// <summary>
/// Configures the schema for the identity framework.
/// </summary>
/// <param name="modelBuilder">
/// The builder being used to construct the model for this context.
/// </param>
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<TMessage>(b => {
b.HasKey(m => m.Id);
b.ToTable("ConsistencyMessages");
});
}
}
}
\ No newline at end of file
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Cap.Consistency; using Microsoft.Extensions.DependencyInjection.Extensions;
using Cap.Consistency.EntityFrameworkCore;
namespace Cap.Consistency.EntityFrameworkCore namespace Cap.Consistency.EntityFrameworkCore
{ {
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency.EntityFrameworkCore namespace Cap.Consistency.EntityFrameworkCore
{ {
...@@ -20,7 +17,6 @@ namespace Cap.Consistency.EntityFrameworkCore ...@@ -20,7 +17,6 @@ namespace Cap.Consistency.EntityFrameworkCore
RollbackFailed = 4 RollbackFailed = 4
} }
public class ConsistencyMessage<TKey> where TKey : IEquatable<TKey> public class ConsistencyMessage<TKey> where TKey : IEquatable<TKey>
{ {
public virtual TKey Id { get; set; } public virtual TKey Id { get; set; }
......
using Cap.Consistency; using System;
using System; using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
namespace Cap.Consistency.EntityFrameworkCore namespace Cap.Consistency.EntityFrameworkCore
{ {
public class ConsistencyMessageStore : ConsistencyMessageStore<ConsistencyMessage, DbContext, string> public class ConsistencyMessageStore : ConsistencyMessageStore<ConsistencyMessage, DbContext, string>
{ {
public ConsistencyMessageStore(DbContext context) : base(context) { } public ConsistencyMessageStore(DbContext context) : base(context) {
}
} }
public class ConsistencyMessageStore<TMessage> : ConsistencyMessageStore<TMessage, DbContext, string> public class ConsistencyMessageStore<TMessage> : ConsistencyMessageStore<TMessage, DbContext, string>
where TMessage : ConsistencyMessage<string> where TMessage : ConsistencyMessage<string>
{ {
public ConsistencyMessageStore(DbContext context) : base(context) { } public ConsistencyMessageStore(DbContext context) : base(context) {
}
} }
public class ConsistencyMessageStore<TMessage, TContext> : ConsistencyMessageStore<TMessage, TContext, string> public class ConsistencyMessageStore<TMessage, TContext> : ConsistencyMessageStore<TMessage, TContext, string>
where TMessage : ConsistencyMessage<string> where TMessage : ConsistencyMessage<string>
where TContext : DbContext where TContext : DbContext
{ {
public ConsistencyMessageStore(TContext context) : base(context) { } public ConsistencyMessageStore(TContext context) : base(context) {
}
} }
public abstract class ConsistencyMessageStore<TMessage, TContext, TKey> : IConsistencyMessageStore<TMessage> public abstract class ConsistencyMessageStore<TMessage, TContext, TKey> : IConsistencyMessageStore<TMessage>
where TMessage : ConsistencyMessage<TKey> where TMessage : ConsistencyMessage<TKey>
where TContext : DbContext where TContext : DbContext
where TKey : IEquatable<TKey> where TKey : IEquatable<TKey>
{ {
private bool _disposed; private bool _disposed;
public ConsistencyMessageStore(TContext context) { public ConsistencyMessageStore(TContext context) {
...@@ -117,7 +112,6 @@ namespace Cap.Consistency.EntityFrameworkCore ...@@ -117,7 +112,6 @@ namespace Cap.Consistency.EntityFrameworkCore
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id); return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
} }
/// <summary> /// <summary>
/// Converts the provided <paramref name="id"/> to its string representation. /// Converts the provided <paramref name="id"/> to its string representation.
/// </summary> /// </summary>
...@@ -189,7 +183,4 @@ namespace Cap.Consistency.EntityFrameworkCore ...@@ -189,7 +183,4 @@ namespace Cap.Consistency.EntityFrameworkCore
_disposed = true; _disposed = true;
} }
} }
} }
\ No newline at end of file
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
"dependencies": { "dependencies": {
"Cap.Consistency": "1.0.0-*", "Cap.Consistency": "1.0.0-*",
"Microsoft.EntityFrameworkCore": "1.1.0-*", "Microsoft.EntityFrameworkCore": "1.1.0-*",
"Microsoft.EntityFrameworkCore.Relational": "1.1.0",
"NETStandard.Library": "1.6.1", "NETStandard.Library": "1.6.1",
"System.ComponentModel.TypeConverter": "4.3.0" "System.ComponentModel.TypeConverter": "4.3.0"
}, },
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Reflection;
using System.Threading.Tasks; using Cap.Consistency.Server.Internal.Infrastructure;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Reflection;
using Cap.Consistency.Server.Internal.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http.Features;
namespace Cap.Consistency.Server namespace Cap.Consistency.Server
{ {
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency.Server namespace Cap.Consistency.Server
{ {
public class ConsistencyServerOptions public class ConsistencyServerOptions
{ {
/// <summary> /// <summary>
/// Enables the Consistency Server options callback to resolve and use services registered by the application during startup. /// Enables the Consistency Server options callback to resolve and use services registered by the application during startup.
/// Typically initialized by <see cref="Cap.Consistency.UseConsistency(Action{ConsistencyServerOptions})"/>. /// Typically initialized by <see cref="Cap.Consistency.UseConsistency(Action{ConsistencyServerOptions})"/>.
/// </summary> /// </summary>
public IServiceProvider ApplicationServices { get; set; } public IServiceProvider ApplicationServices { get; set; }
/// <summary> /// <summary>
/// The amount of time after the server begins shutting down before connections will be forcefully closed. /// The amount of time after the server begins shutting down before connections will be forcefully closed.
/// Kestrel will wait for the duration of the timeout for any ongoing request processing to complete before /// Kestrel will wait for the duration of the timeout for any ongoing request processing to complete before
......
using Microsoft.AspNetCore.Hosting; using System;
using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency.Server namespace Cap.Consistency.Server
{ {
......
...@@ -27,8 +27,7 @@ namespace Cap.Consistency.Server.Internal.Infrastructure ...@@ -27,8 +27,7 @@ namespace Cap.Consistency.Server.Internal.Infrastructure
protected readonly ILogger _logger; protected readonly ILogger _logger;
static ConsistencyTrace() static ConsistencyTrace() {
{
_connectionStart = LoggerMessage.Define<string>(LogLevel.Debug, 1, @"Connection id ""{ConnectionId}"" started."); _connectionStart = LoggerMessage.Define<string>(LogLevel.Debug, 1, @"Connection id ""{ConnectionId}"" started.");
_connectionStop = LoggerMessage.Define<string>(LogLevel.Debug, 2, @"Connection id ""{ConnectionId}"" stopped."); _connectionStop = LoggerMessage.Define<string>(LogLevel.Debug, 2, @"Connection id ""{ConnectionId}"" stopped.");
// ConnectionRead: Reserved: 3 // ConnectionRead: Reserved: 3
...@@ -50,120 +49,96 @@ namespace Cap.Consistency.Server.Internal.Infrastructure ...@@ -50,120 +49,96 @@ namespace Cap.Consistency.Server.Internal.Infrastructure
_requestProcessingError = LoggerMessage.Define<string>(LogLevel.Information, 20, @"Connection id ""{ConnectionId}"" request processing ended abnormally."); _requestProcessingError = LoggerMessage.Define<string>(LogLevel.Information, 20, @"Connection id ""{ConnectionId}"" request processing ended abnormally.");
} }
public ConsistencyTrace(ILogger logger) public ConsistencyTrace(ILogger logger) {
{
_logger = logger; _logger = logger;
} }
public virtual void ConnectionStart(string connectionId) public virtual void ConnectionStart(string connectionId) {
{
_connectionStart(_logger, connectionId, null); _connectionStart(_logger, connectionId, null);
} }
public virtual void ConnectionStop(string connectionId) public virtual void ConnectionStop(string connectionId) {
{
_connectionStop(_logger, connectionId, null); _connectionStop(_logger, connectionId, null);
} }
public virtual void ConnectionRead(string connectionId, int count) public virtual void ConnectionRead(string connectionId, int count) {
{
} }
public virtual void ConnectionPause(string connectionId) public virtual void ConnectionPause(string connectionId) {
{
_connectionPause(_logger, connectionId, null); _connectionPause(_logger, connectionId, null);
} }
public virtual void ConnectionResume(string connectionId) public virtual void ConnectionResume(string connectionId) {
{
_connectionResume(_logger, connectionId, null); _connectionResume(_logger, connectionId, null);
} }
public virtual void ConnectionReadFin(string connectionId) public virtual void ConnectionReadFin(string connectionId) {
{
_connectionReadFin(_logger, connectionId, null); _connectionReadFin(_logger, connectionId, null);
} }
public virtual void ConnectionWriteFin(string connectionId) public virtual void ConnectionWriteFin(string connectionId) {
{
_connectionWriteFin(_logger, connectionId, null); _connectionWriteFin(_logger, connectionId, null);
} }
public virtual void ConnectionWroteFin(string connectionId, int status) public virtual void ConnectionWroteFin(string connectionId, int status) {
{
_connectionWroteFin(_logger, connectionId, status, null); _connectionWroteFin(_logger, connectionId, status, null);
} }
public virtual void ConnectionKeepAlive(string connectionId) public virtual void ConnectionKeepAlive(string connectionId) {
{
_connectionKeepAlive(_logger, connectionId, null); _connectionKeepAlive(_logger, connectionId, null);
} }
public virtual void ConnectionDisconnect(string connectionId) public virtual void ConnectionDisconnect(string connectionId) {
{
_connectionDisconnect(_logger, connectionId, null); _connectionDisconnect(_logger, connectionId, null);
} }
public virtual void ConnectionWrite(string connectionId, int count) public virtual void ConnectionWrite(string connectionId, int count) {
{
// Don't log for now since this could be *too* verbose. // Don't log for now since this could be *too* verbose.
// Reserved: Event ID 11 // Reserved: Event ID 11
} }
public virtual void ConnectionWriteCallback(string connectionId, int status) public virtual void ConnectionWriteCallback(string connectionId, int status) {
{
// Don't log for now since this could be *too* verbose. // Don't log for now since this could be *too* verbose.
// Reserved: Event ID 12 // Reserved: Event ID 12
} }
public virtual void ApplicationError(string connectionId, Exception ex) public virtual void ApplicationError(string connectionId, Exception ex) {
{
_applicationError(_logger, connectionId, ex); _applicationError(_logger, connectionId, ex);
} }
public virtual void ConnectionError(string connectionId, Exception ex) public virtual void ConnectionError(string connectionId, Exception ex) {
{
_connectionError(_logger, connectionId, ex); _connectionError(_logger, connectionId, ex);
} }
public virtual void ConnectionDisconnectedWrite(string connectionId, int count, Exception ex) public virtual void ConnectionDisconnectedWrite(string connectionId, int count, Exception ex) {
{
_connectionDisconnectedWrite(_logger, connectionId, count, ex); _connectionDisconnectedWrite(_logger, connectionId, count, ex);
} }
public virtual void ConnectionHeadResponseBodyWrite(string connectionId, long count) public virtual void ConnectionHeadResponseBodyWrite(string connectionId, long count) {
{
_connectionHeadResponseBodyWrite(_logger, connectionId, count, null); _connectionHeadResponseBodyWrite(_logger, connectionId, count, null);
} }
public virtual void NotAllConnectionsClosedGracefully() public virtual void NotAllConnectionsClosedGracefully() {
{
_notAllConnectionsClosedGracefully(_logger, null); _notAllConnectionsClosedGracefully(_logger, null);
} }
public virtual void ConnectionReset(string connectionId) public virtual void ConnectionReset(string connectionId) {
{
_connectionReset(_logger, connectionId, null); _connectionReset(_logger, connectionId, null);
} }
public virtual void RequestProcessingError(string connectionId, Exception ex) public virtual void RequestProcessingError(string connectionId, Exception ex) {
{
_requestProcessingError(_logger, connectionId, ex); _requestProcessingError(_logger, connectionId, ex);
} }
public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {
{
_logger.Log(logLevel, eventId, state, exception, formatter); _logger.Log(logLevel, eventId, state, exception, formatter);
} }
public virtual bool IsEnabled(LogLevel logLevel) public virtual bool IsEnabled(LogLevel logLevel) {
{
return _logger.IsEnabled(logLevel); return _logger.IsEnabled(logLevel);
} }
public virtual IDisposable BeginScope<TState>(TState state) public virtual IDisposable BeginScope<TState>(TState state) {
{
return _logger.BeginScope(state); return _logger.BeginScope(state);
} }
} }
......
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
......
using System; namespace Cap.Consistency
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency
{ {
public class BrokerOptions public class BrokerOptions
{ {
} }
} }
\ No newline at end of file
using Cap.Consistency; using System;
using Cap.Consistency;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
......
using Microsoft.Extensions.DependencyInjection; using System;
using System;
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace Cap.Consistency namespace Cap.Consistency
{ {
...@@ -60,12 +60,9 @@ namespace Cap.Consistency ...@@ -60,12 +60,9 @@ namespace Cap.Consistency
return AddScoped(messageManagerType, customType); return AddScoped(messageManagerType, customType);
} }
private ConsistencyBuilder AddScoped(Type serviceType, Type concreteType) { private ConsistencyBuilder AddScoped(Type serviceType, Type concreteType) {
Services.AddScoped(serviceType, concreteType); Services.AddScoped(serviceType, concreteType);
return this; return this;
} }
} }
} }
\ No newline at end of file
using Microsoft.AspNetCore.Http; using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Cap.Consistency namespace Cap.Consistency
{ {
......
using Cap.Consistency; using Cap.Consistency;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
{ {
/// <summary> /// <summary>
/// Represents all the options you can use to configure the system. /// Represents all the options you can use to configure the system.
/// </summary> /// </summary>
public class ConsistencyOptions public class ConsistencyOptions
{ {
/// <summary> /// <summary>
/// Gets or sets the <see cref="BrokerOptions"/> for the consistency system. /// Gets or sets the <see cref="BrokerOptions"/> for the consistency system.
/// </summary> /// </summary>
......
using Cap.Consistency; using System;
using Cap.Consistency;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
...@@ -11,7 +11,6 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -11,7 +11,6 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary> /// </summary>
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
/// <summary> /// <summary>
/// Adds and configures the consistence services for the consitence. /// Adds and configures the consistence services for the consitence.
/// </summary> /// </summary>
...@@ -30,7 +29,6 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -30,7 +29,6 @@ namespace Microsoft.Extensions.DependencyInjection
/// <returns>An <see cref="ConsistencyBuilder"/> for application services.</returns> /// <returns>An <see cref="ConsistencyBuilder"/> for application services.</returns>
public static ConsistencyBuilder AddConsistency<TMessage>(this IServiceCollection services, Action<ConsistencyOptions> setupAction) public static ConsistencyBuilder AddConsistency<TMessage>(this IServiceCollection services, Action<ConsistencyOptions> setupAction)
where TMessage : class { where TMessage : class {
services.TryAddSingleton<ConsistencyMarkerService>(); services.TryAddSingleton<ConsistencyMarkerService>();
services.TryAddScoped<ConsistencyMessageManager<TMessage>, ConsistencyMessageManager<TMessage>>(); services.TryAddScoped<ConsistencyMessageManager<TMessage>, ConsistencyMessageManager<TMessage>>();
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency.EntityFrameworkCore.Test
{
public class Class1
{
public Class1()
{
}
}
}
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Cap.Consistency.EntityFrameworkCore.Test
{
public static class DbUtil
{
public static IServiceCollection ConfigureDbServices(string connectionString, IServiceCollection services = null) {
return ConfigureDbServices<ConsistencyDbContext>(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;
using Cap.Consistency.EntityFrameworkCore.Test.Utilities;
using Cap.Consistency.Test;
using Microsoft.AspNetCore.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Cap.Consistency.EntityFrameworkCore.Test
{
public class MessageStoreWithGenericsTest : MessageManagerTestBase<MessageWithGenerics, string>, IClassFixture<ScratchDatabaseFixture>
{
private readonly ScratchDatabaseFixture _fixture;
public MessageStoreWithGenericsTest(ScratchDatabaseFixture fixture) {
_fixture = fixture;
}
protected override void AddMessageStore(IServiceCollection services, object context = null) {
services.AddSingleton<IConsistencyMessageStore<MessageWithGenerics>>(new MessageStoreWithGenerics((ContextWithGenerics)context));
}
protected override object CreateTestContext() {
return CreateContext();
}
public ContextWithGenerics CreateContext() {
var db = DbUtil.Create<ContextWithGenerics>(_fixture.ConnectionString);
db.Database.EnsureCreated();
return db;
}
protected override MessageWithGenerics CreateTestMessage(string payload = "") {
return new MessageWithGenerics() {
Payload = payload,
SendTime = DateTime.Now,
Status = MessageStatus.WaitForSend,
UpdateTime = DateTime.Now
};
}
protected override bool ShouldSkipDbTests() {
return TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows;
}
}
public class MessageWithGenerics : ConsistencyMessage
{
}
public class MessageStoreWithGenerics : ConsistencyMessageStore<MessageWithGenerics>
{
public MessageStoreWithGenerics(DbContext context) : base(context) {
}
}
public class ContextWithGenerics : ConsistencyDbContext<MessageWithGenerics, string>
{
public ContextWithGenerics(DbContextOptions options) : base(options) {
}
}
}
\ No newline at end of file
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
......
using System;
using Microsoft.EntityFrameworkCore.Internal;
namespace Cap.Consistency.EntityFrameworkCore.Test.Utilities
{
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 Cap.Consistency.EntityFrameworkCore.Test.Utilities
{
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 Cap.Consistency.EntityFrameworkCore.Test.Utilities
{
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=(localdb)\\MSSqlLocaldb;Integrated Security=true;MultipleActiveResultSets=true;Connect Timeout=30"
}
}
}
\ No newline at end of file
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"buildOptions": { "buildOptions": {
"warningsAsErrors": true, "warningsAsErrors": true,
"compile": { "compile": {
"include": "../Shared/TestConsistencyMessage.cs" "include": "../Shared/*.cs"
} }
}, },
"dependencies": { "dependencies": {
...@@ -15,7 +15,15 @@ ...@@ -15,7 +15,15 @@
"Cap.Consistency": "1.0.0-*", "Cap.Consistency": "1.0.0-*",
"Moq": "4.6.36-*", "Moq": "4.6.36-*",
"Microsoft.Extensions.Logging": "1.1.0-*", "Microsoft.Extensions.Logging": "1.1.0-*",
"Cap.Consistency.EntityFrameworkCore": "1.0.0-*" "Cap.Consistency.EntityFrameworkCore": "1.0.0-*",
"Microsoft.Extensions.Configuration.Abstractions": "1.1.0-*",
"Microsoft.Extensions.Configuration": "1.1.0-*",
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0-*",
"Microsoft.Extensions.Configuration.Json": "1.1.0-*",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0-*",
"System.Data.SqlClient": "4.3.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.1.0-*",
"Microsoft.AspNetCore.Testing": "1.2.0-preview1-22815"
}, },
"frameworks": { "frameworks": {
......
using Microsoft.Extensions.DependencyInjection; using System;
using System; using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
using System.Threading;
namespace Cap.Consistency.Test namespace Cap.Consistency.Test
{ {
......
using Microsoft.AspNetCore.Http; using System;
using Microsoft.Extensions.DependencyInjection; using System.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq; using Moq;
using System.Threading; using Xunit;
namespace Cap.Consistency.Test namespace Cap.Consistency.Test
{ {
......
using System; namespace Cap.Consistency.Test
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Cap.Consistency.Test
{ {
public class ConsistencyOptionsTest public class ConsistencyOptionsTest
{ {
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
......
using System; using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit; using Xunit;
namespace Cap.Consistency.Test namespace Cap.Consistency.Test
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Cap.Consistency.Test
{
public abstract class MessageManagerTestBase<TMessage> : MessageManagerTestBase<TMessage, string>
where TMessage : class
{
}
public abstract class MessageManagerTestBase<TMessage, TKey>
where TMessage : class
where TKey : IEquatable<TKey>
{
private const string NullValue = "(null)";
protected virtual bool ShouldSkipDbTests() {
return false;
}
protected virtual void SetupMessageServices(IServiceCollection services, object context = null) {
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddConsistency<TMessage>();
AddMessageStore(services, context);
services.AddSingleton<ILogger<ConsistencyMessageManager<TMessage>>>(new TestLogger<ConsistencyMessageManager<TMessage>>());
}
protected virtual ConsistencyMessageManager<TMessage> CreateManager(object context = null, IServiceCollection services = null, Action<IServiceCollection> configureServices = null) {
if (services == null) {
services = new ServiceCollection();
}
if (context == null) {
context = CreateTestContext();
}
SetupMessageServices(services, context);
configureServices?.Invoke(services);
return services.BuildServiceProvider().GetService<ConsistencyMessageManager<TMessage>>();
}
protected abstract object CreateTestContext();
protected abstract TMessage CreateTestMessage(string payload = "");
protected abstract void AddMessageStore(IServiceCollection services, object context = null);
[Fact]
public async Task CanDeleteMessage() {
if (ShouldSkipDbTests()) {
return;
}
var manager = CreateManager();
var message = CreateTestMessage();
var operateResult = await manager.CreateAsync(message);
Assert.NotNull(operateResult);
Assert.True(operateResult.Succeeded);
var messageId = await manager.GetMessageIdAsync(message);
operateResult = await manager.DeleteMessageAsync(message);
Assert.Null(await manager.FindByIdAsync(messageId));
}
[Fact]
public async Task CanFindById() {
if (ShouldSkipDbTests()) {
return;
}
var manager = CreateManager();
var message = CreateTestMessage();
var operateResult = await manager.CreateAsync(message);
Assert.NotNull(operateResult);
Assert.True(operateResult.Succeeded);
var messageId = await manager.GetMessageIdAsync(message);
Assert.NotNull(await manager.FindByIdAsync(messageId));
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Cap.Consistency.Test
{
public interface ITestLogger
{
IList<string> LogMessages { get; }
}
public class TestLogger<TName> : ILogger<TName>, ITestLogger
{
public IList<string> LogMessages { get; } = new List<string>();
public IDisposable BeginScope<TState>(TState state) {
LogMessages.Add(state?.ToString());
return null;
}
public bool IsEnabled(LogLevel logLevel) {
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {
if (formatter == null) {
LogMessages.Add(state.ToString());
}
else {
LogMessages.Add(formatter(state, exception));
}
}
}
}
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