Skip to content

Commit

Permalink
Entity Framework Support (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle authored Jan 5, 2025
1 parent 483eff1 commit f1dda9b
Show file tree
Hide file tree
Showing 37 changed files with 595 additions and 216 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
# Windows
- build: win-x64
os: windows-latest
test: --filter Category!=Docker

# Linux
- build: linux-x64
Expand All @@ -36,6 +37,7 @@ jobs:
# macOS
- build: macos-aarch64
os: macos-latest
test: --filter Category!=Docker

steps:
- name: Checkout
Expand Down Expand Up @@ -63,6 +65,6 @@ jobs:

- name: Run tests server
run: |
dotnet test Tests/YDotNet.Tests.Server.Unit
dotnet test Tests/YDotNet.Tests.Server.Unit ${{ matrix.test }}
env:
RUST_BACKTRACE: 1
9 changes: 1 addition & 8 deletions Demo/Callback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,8 @@

namespace Demo;

public sealed class Callback : IDocumentCallback
public sealed class Callback(ILogger<Callback> log) : IDocumentCallback
{
private readonly ILogger<Callback> log;

public Callback(ILogger<Callback> log)
{
this.log = log;
}

public ValueTask OnAwarenessUpdatedAsync(ClientAwarenessEvent @event)
{
log.LogInformation("Client {clientId} awareness changed.", @event.Context.ClientId);
Expand Down
13 changes: 13 additions & 0 deletions Demo/Db/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
using YDotNet.Server.EntityFramework;

namespace Demo.Db;

public class AppDbContext(DbContextOptions options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseYDotNet();
base.OnModelCreating(modelBuilder);
}
}
22 changes: 22 additions & 0 deletions Demo/Db/DbInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;

namespace Demo.Db;

public class DbInitializer(IDbContextFactory<AppDbContext> dbContextFactory) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
await using var context = await dbContextFactory.CreateDbContextAsync(cancellationToken);

var creator = (RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
await creator.EnsureCreatedAsync(cancellationToken);
}

public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
17 changes: 17 additions & 0 deletions Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Demo.Db;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using StackExchange.Redis;
using YDotNet.Server;
using YDotNet.Server.EntityFramework;
using YDotNet.Server.MongoDB;

namespace Demo;
Expand Down Expand Up @@ -50,6 +53,20 @@ public static void Main(string[] args)
builder.Configuration["Storage:Redis:ConnectionString"]!);
});

break;
}

case "Postgres":
{
builder.Services.AddHostedService<DbInitializer>();
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseNpgsql(
builder.Configuration["Storage:Postgres:ConnectionString"]!);
});

yDotNet.AddEntityFrameworkStorage<AppDbContext>();

break;
}
}
Expand Down
3 changes: 3 additions & 0 deletions Demo/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
},
"Redis": {
"ConnectionString": "localhost:6379"
},
"Postgres": {
"ConnectionString": "Host=localhost;Port=5432;Database=ydotnet;Username=postgres;Password=password"
}
}
}
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>0.4.1</Version>
<Version>0.4.2</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
Expand Down
5 changes: 3 additions & 2 deletions Tests/YDotNet.Tests.Server.Unit/DelayedWriterTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
namespace YDotNet.Tests.Server.Unit;

using NUnit.Framework;
using YDotNet.Server.Internal;

namespace YDotNet.Tests.Server.Unit;

public class DelayedWriterTests
{
[Test]
Expand Down Expand Up @@ -81,6 +81,7 @@ public async Task WriteAfterDelayOnce()
{
sut.Ping();
}

await Task.Delay(300);

// Assert
Expand Down
13 changes: 9 additions & 4 deletions Tests/YDotNet.Tests.Server.Unit/DocumentContainerTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
namespace YDotNet.Tests.Server.Unit;

using FakeItEasy;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
Expand All @@ -6,8 +8,6 @@
using YDotNet.Server.Internal;
using YDotNet.Server.Storage;

namespace YDotNet.Tests.Server.Unit;

public class DocumentContainerTests
{
private readonly IDocumentStorage documentStorage = A.Fake<IDocumentStorage>();
Expand All @@ -31,7 +31,7 @@ await sut.ApplyUpdateReturnAsync(async doc =>
map.Insert(transaction, "key", Input.Double(42));
}

await Task.Delay(100);
await Task.Delay(100).ConfigureAwait(false);
return true;
});

Expand All @@ -43,7 +43,12 @@ private DocumentContainer CreateSut(DocumentManagerOptions? options)
{
options ??= new DocumentManagerOptions();

return new DocumentContainer(name, documentStorage, documentCallback, documentManager, options,
return new DocumentContainer(
name,
documentStorage,
documentCallback,
documentManager,
options,
A.Fake<ILogger>());
}
}
60 changes: 60 additions & 0 deletions Tests/YDotNet.Tests.Server.Unit/DocumentStorageTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace YDotNet.Tests.Server.Unit;

using NUnit.Framework;
using YDotNet.Server.Storage;

public abstract class DocumentStorageTests
{
protected abstract IDocumentStorage CreateSut();

[Test]
public async Task GetDocData()
{
// Arrange
var sut = CreateSut();

// Act
var received = await sut.GetDocAsync(Guid.NewGuid().ToString());

// Assert
Assert.That(received, Is.Null);
}

[Test]
public async Task InsertDocument()
{
// Arrange
var sut = CreateSut();

var docName = Guid.NewGuid().ToString();
var docData = new byte[] { 1, 2, 3, 4 };

// Act
await sut.StoreDocAsync(docName, docData);

// Assert
var received = await sut.GetDocAsync(docName);

Assert.That(received, Is.EqualTo(docData));
}

[Test]
public async Task UpdateDocument()
{
// Arrange
var sut = CreateSut();

var docName = Guid.NewGuid().ToString();
var docData1 = new byte[] { 1, 2, 3, 4 };
var docData2 = new byte[] { 5, 6, 7, 8 };

// Act
await sut.StoreDocAsync(docName, docData1);
await sut.StoreDocAsync(docName, docData2);

// Assert
var received = await sut.GetDocAsync(docName);

Assert.That(received, Is.EqualTo(docData2));
}
}
80 changes: 80 additions & 0 deletions Tests/YDotNet.Tests.Server.Unit/EFDocumentStorageFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace YDotNet.Tests.Server.Unit;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NUnit.Framework;
using Testcontainers.PostgreSql;
using YDotNet.Server.EntityFramework;
using YDotNet.Server.Storage;

[TestFixture]
[Category("Docker")]
public class EFDocumentStorageFixture : DocumentStorageTests
{
private readonly PostgreSqlContainer postgres = new PostgreSqlBuilder().Build();
private IServiceProvider services;

public class AppDbContext(DbContextOptions options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseYDotNet();
base.OnModelCreating(modelBuilder);
}
}

[OneTimeSetUp]
public async Task OneTimeSetup()
{
await postgres.StartAsync();
}

[OneTimeTearDown]
public async Task OneTimeTeardown()
{
await postgres.StopAsync();
}

[SetUp]
public async Task Setup()
{
services = new ServiceCollection()
.AddLogging()
.AddDbContext<AppDbContext>(options =>
{
options.UseNpgsql(postgres.GetConnectionString());
})
.AddYDotNet()
.AddEntityFrameworkStorage<AppDbContext>()
.Services
.BuildServiceProvider();

foreach (var service in services.GetRequiredService<IEnumerable<IHostedService>>())
{
await service.StartAsync(default);
}

var factory = services.GetRequiredService<IDbContextFactory<AppDbContext>>();
var context = await factory.CreateDbContextAsync();
var creator = (RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();

await creator.EnsureCreatedAsync();
}

[TearDown]
public async Task Teardown()
{
foreach (var service in services.GetRequiredService<IEnumerable<IHostedService>>())
{
await service.StopAsync(default);
}
}

protected override IDocumentStorage CreateSut()
{
return services.GetRequiredService<IDocumentStorage>();
}
}
60 changes: 60 additions & 0 deletions Tests/YDotNet.Tests.Server.Unit/MongoDocumentStorageFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace YDotNet.Tests.Server.Unit;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MongoDB.Driver;
using NUnit.Framework;
using Testcontainers.MongoDb;
using YDotNet.Server.MongoDB;
using YDotNet.Server.Storage;

[TestFixture]
[Category("Docker")]
public class MongoDocumentStorageFixture : DocumentStorageTests
{
private readonly MongoDbContainer mongo = new MongoDbBuilder().Build();
private IServiceProvider services;

[OneTimeSetUp]
public async Task OneTimeSetup()
{
await mongo.StartAsync();
}

[OneTimeTearDown]
public async Task OneTimeTeardown()
{
await mongo.StopAsync();
}

[SetUp]
public async Task Setup()
{
services = new ServiceCollection()
.AddSingleton<IMongoClient>(_ => new MongoClient(mongo.GetConnectionString()))
.AddLogging()
.AddYDotNet()
.AddMongoStorage()
.Services
.BuildServiceProvider();

foreach (var service in services.GetRequiredService<IEnumerable<IHostedService>>())
{
await service.StartAsync(default);
}
}

[TearDown]
public async Task Teardown()
{
foreach (var service in services.GetRequiredService<IEnumerable<IHostedService>>())
{
await service.StopAsync(default);
}
}

protected override IDocumentStorage CreateSut()
{
return services.GetRequiredService<IDocumentStorage>();
}
}
Loading

0 comments on commit f1dda9b

Please sign in to comment.