diff --git a/src/Session.Tests/MongoSessionProviderTests.cs b/src/Session.Tests/MongoSessionProviderTests.cs new file mode 100644 index 0000000..5599b17 --- /dev/null +++ b/src/Session.Tests/MongoSessionProviderTests.cs @@ -0,0 +1,138 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson; +using MongoDB.Driver; +using MongoDB.Extensions.Context; +using Squadron; +using Xunit; + +namespace MongoDB.Extensions.Session.Tests; + +public class MongoSessionProviderTests : IClassFixture +{ + private readonly IServiceProvider _serviceProvider; + + public MongoSessionProviderTests(MongoReplicaSetResource mongoResource) + { + var mongoOptions = new MongoOptions + { + ConnectionString = mongoResource.ConnectionString, + DatabaseName = mongoResource.CreateDatabase().DatabaseNamespace.DatabaseName + }; + + _serviceProvider = new ServiceCollection() + .AddSingleton(mongoOptions) + .AddMongoSessionProvider() + .BuildServiceProvider(); + } + + [Fact] + public async Task BeginTransactionAsync_ShouldBeginTransaction() + { + // Arrange + ISessionProvider sessionProvider = _serviceProvider + .GetRequiredService>(); + + // Act + ITransactionSession transactionSession = await sessionProvider + .BeginTransactionAsync(CancellationToken.None); + + // Assert + transactionSession.Should().NotBeNull(); + IClientSessionHandle clientSessionHandle = transactionSession.GetSessionHandle(); + clientSessionHandle.ServerSession.Id["id"].AsGuid.Should().NotBeEmpty(); + clientSessionHandle.IsInTransaction.Should().BeTrue(); + } + + [Fact] + public async Task StartSessionAsync_ShouldStartSession() + { + // Arrange + ISessionProvider sessionProvider = _serviceProvider + .GetRequiredService>(); + + // Act + ISession session = await sessionProvider + .StartSessionAsync(CancellationToken.None); + + // Assert + session.Should().NotBeNull(); + IClientSessionHandle clientSessionHandle = session.GetSessionHandle(); + clientSessionHandle.ServerSession.Id["id"].AsGuid.Should().NotBeEmpty(); + clientSessionHandle.IsInTransaction.Should().BeFalse(); + } + + [Fact] + public async Task MongoSession_Dispose_ShouldDisposeSession() + { + // Arrange + ISessionProvider sessionProvider = _serviceProvider + .GetRequiredService>(); + + ISession session = await sessionProvider + .StartSessionAsync(CancellationToken.None); + + // Act + session.Dispose(); + + // Assert + IClientSessionHandle clientSessionHandle = session.GetSessionHandle(); + Assert.Throws(() => clientSessionHandle.ServerSession); + } + + [Fact] + public async Task MongoTransactionSession_Dispose_ShouldDisposeSession() + { + // Arrange + ISessionProvider sessionProvider = _serviceProvider + .GetRequiredService>(); + + ITransactionSession transactionSession = await sessionProvider + .BeginTransactionAsync(CancellationToken.None); + + // Act + transactionSession.Dispose(); + + // Assert + IClientSessionHandle clientSessionHandle = transactionSession.GetSessionHandle(); + Assert.Throws(() => clientSessionHandle.ServerSession); + } + + [Fact] + public async Task MongoTransactionSession_NotCommitting_ShouldNotAffectDatabase() + { + // Arrange + ISessionProvider sessionProvider = _serviceProvider + .GetRequiredService>(); + + ITransactionSession transactionSession = await sessionProvider + .BeginTransactionAsync(CancellationToken.None); + TestDbContext context = _serviceProvider.GetRequiredService(); + IMongoCollection collection = context.CreateCollection(); + await collection.InsertOneAsync(transactionSession.GetSessionHandle(), new BsonDocument()); + + // Act + // Not committing the transaction + + // Assert + (await collection + .Find(FilterDefinition.Empty) + .ToListAsync()) + .Count.Should().Be(0); + } + + private class TestDbContext : MongoDbContext + { + public TestDbContext(MongoOptions mongoOptions) + : base(mongoOptions) + { + } + + protected override void OnConfiguring(IMongoDatabaseBuilder mongoDatabaseBuilder) + { + } + } +} diff --git a/src/Session/Internal/MongoSession.cs b/src/Session/Internal/MongoSession.cs index 8a25241..e149d63 100644 --- a/src/Session/Internal/MongoSession.cs +++ b/src/Session/Internal/MongoSession.cs @@ -7,7 +7,6 @@ namespace MongoDB.Extensions.Session; internal sealed class MongoSession : ISession { - private readonly IClientSessionHandle _session; private bool _disposed; private static TransactionOptions TransactionOptions { get; } = new( @@ -18,14 +17,16 @@ internal sealed class MongoSession : ISession public MongoSession(IClientSessionHandle clientSession) { - _session = clientSession; + Session = clientSession; } + public IClientSessionHandle Session { get; } + public Task WithTransactionAsync( Func> action, CancellationToken cancellationToken) { - return _session.WithTransactionAsync( + return Session.WithTransactionAsync( (_, ct) => action(this, ct), TransactionOptions, cancellationToken); @@ -37,7 +38,7 @@ private void Dispose(bool disposing) { if (disposing) { - _session.Dispose(); + Session.Dispose(); } _disposed = true; diff --git a/src/Session/Internal/MongoTransactionSession.cs b/src/Session/Internal/MongoTransactionSession.cs index c27e1da..9835aa7 100644 --- a/src/Session/Internal/MongoTransactionSession.cs +++ b/src/Session/Internal/MongoTransactionSession.cs @@ -7,7 +7,6 @@ namespace MongoDB.Extensions.Session; internal class MongoTransactionSession : ITransactionSession { - private readonly IClientSessionHandle _session; private readonly CancellationToken _cancellationToken; private bool _disposed; @@ -15,13 +14,15 @@ public MongoTransactionSession( IClientSessionHandle clientSession, CancellationToken cancellationToken) { - _session = clientSession; + Session = clientSession; _cancellationToken = cancellationToken; } + public IClientSessionHandle Session { get; } + public async Task CommitAsync() { - await _session.CommitTransactionAsync(_cancellationToken); + await Session.CommitTransactionAsync(_cancellationToken); } protected virtual void Dispose(bool disposing) @@ -30,7 +31,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - _session.Dispose(); + Session.Dispose(); } _disposed = true; diff --git a/src/Session/TransactionSessionExtensions.cs b/src/Session/TransactionSessionExtensions.cs new file mode 100644 index 0000000..97d7eb3 --- /dev/null +++ b/src/Session/TransactionSessionExtensions.cs @@ -0,0 +1,31 @@ +using System; +using MongoDB.Driver; + +namespace MongoDB.Extensions.Session; + +public static class TransactionSessionExtensions +{ + public static IClientSessionHandle GetSessionHandle( + this ITransactionSession session) + { + if (session is MongoTransactionSession mongoTransactionSession) + { + return mongoTransactionSession.Session; + } + + throw new InvalidOperationException( + $"Unknown session type {session.GetType().Name}"); + } + + public static IClientSessionHandle GetSessionHandle( + this ISession session) + { + if (session is MongoSession mongoSession) + { + return mongoSession.Session; + } + + throw new InvalidOperationException( + $"Unknown session type {session.GetType().Name}"); + } +}