Skip to content
This repository has been archived by the owner on Aug 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #206 from itmo-is-dev/fix/querying
Browse files Browse the repository at this point in the history
fix: multiple operations error
  • Loading branch information
ronimizy authored Jun 15, 2023
2 parents 74d995d + 96fe5eb commit 1c4c1bd
Show file tree
Hide file tree
Showing 47 changed files with 417 additions and 503 deletions.
3 changes: 2 additions & 1 deletion src/ITMO.Dev.ASAP/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using ITMO.Dev.ASAP.Presentation.Rpc.Extensions;
using ITMO.Dev.ASAP.Presentation.Services.Extensions;
using Microsoft.EntityFrameworkCore;
using Serilog;
using ConfigurationBuilder = FluentSerialization.ConfigurationBuilder;

namespace ITMO.Dev.ASAP.Extensions;
Expand Down Expand Up @@ -47,7 +48,7 @@ internal static IServiceCollection ConfigureServiceCollection(
.AddHandlers(configuration)
.AddDatabaseContext(o => o
.UseNpgsql(applicationDatabaseConnectionString)
.UseLazyLoadingProxies());
.UseLoggerFactory(LoggerFactory.Create(x => x.AddSerilog().SetMinimumLevel(LogLevel.Trace))));

serviceCollection.AddIdentityConfiguration(
identityConfigurationSection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task<Response> Handle(Command request, CancellationToken cancellati
IEnumerable<SubjectCourseGroupCreated.Notification> notifications = dtos
.Select(g => new SubjectCourseGroupCreated.Notification(g));

IEnumerable<Task> tasks = notifications.Select(x => _publisher.PublishAsync(x, cancellationToken));
IEnumerable<Task> tasks = notifications.Select(x => _publisher.Publish(x, default));
await Task.WhenAll(tasks);

return new Response(dtos);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ public GetSubjectCourseStudentsHandler(IPersistenceContext context)

public async Task<Response> Handle(Query request, CancellationToken cancellationToken)
{
IReadOnlyCollection<Student> students = await _context.Students
.GetStudentsBySubjectCourseIdAsync(request.SubjectCourseIds, cancellationToken)
.ToArrayAsync(cancellationToken);
IAsyncEnumerable<Student> students = _context.Students
.GetStudentsBySubjectCourseIdAsync(request.SubjectCourseIds, cancellationToken);

Guid[] ids = students.Select(x => x.UserId).ToArray();
Guid[] ids = await students.Select(x => x.UserId).ToArrayAsync(cancellationToken);

return new Response(ids);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public TitleUpdatedEvent UpdateTitle(string value)
foreach (StudentGroup studentGroup in groupsToAdd)
{
var subjectCourseGroup = new SubjectCourseGroup(subjectCourseId: Id, studentGroupId: studentGroup.Id);
subjectCourseGroups.Add(subjectCourseGroup);

IEnumerable<GroupAssignmentCreatedEvent> assignmentEvents = _assignments
.Select(x => x.AddGroup(studentGroup.Info, DateOnly.FromDateTime(DateTime.UnixEpoch)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }

protected DatabaseContext(DbContextOptions options) : base(options) { }

public DbSet<UserModel> Users { get; protected init; } = null!;

public DbSet<StudentModel> Students { get; protected init; } = null!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public IAsyncEnumerable<Student> QueryAsync(StudentQuery query, CancellationToke
|| EF.Functions.ILike(student.User.LastName, query.FullNamePattern));
}

queryable = queryable
.Include(x => x.User)
.ThenInclude(x => x.Associations);

return queryable.AsAsyncEnumerable().Select(StudentMapper.MapTo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public ValueTask VisitAsync(SubjectCourseGroupRemovedEvent evt, CancellationToke
EntityEntry<SubjectCourseGroupModel> entry = RepositoryTools.GetEntry(
_context,
x => x.SubjectCourseId.Equals(evt.SubjectCourse.Id) && x.StudentGroupId.Equals(evt.StudentGroupId),
() => new SubjectCourseGroupModel(evt.StudentGroupId, evt.SubjectCourse.Id));
() => new SubjectCourseGroupModel(evt.SubjectCourse.Id, evt.StudentGroupId));

entry.State = EntityState.Deleted;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public IAsyncEnumerable<Submission> QueryAsync(SubmissionQuery query, Cancellati
{
IQueryable<SubmissionModel> queryable = ApplyQuery(_context.Submissions, query);

queryable = queryable
.Include(x => x.Student)
.ThenInclude(x => x.User)
.ThenInclude(x => x.Associations);

var finalQueryable = queryable.Select(submission => new
{
submission,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public static IServiceCollection AddDatabaseSeeders(this IServiceCollection coll
return collection;
}

public static async Task UseDatabaseSeeders(
public static async Task UseDatabaseSeeders<TContext>(
this IServiceScope scope,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default) where TContext : DatabaseContext
{
DatabaseContext context = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
DatabaseContext context = scope.ServiceProvider.GetRequiredService<TContext>();

IEnumerable<IDatabaseSeeder> seeders = scope.ServiceProvider
.GetServices<IDatabaseSeeder>()
Expand Down
16 changes: 3 additions & 13 deletions tests/ITMO.Dev.ASAP.Tests.Core/Application/RateSubmissionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,19 @@
namespace ITMO.Dev.ASAP.Tests.Core.Application;

[Collection(nameof(CoreDatabaseCollectionFixture))]
public class RateSubmissionTest : TestBase, IAsyncDisposeLifetime
public class RateSubmissionTest : CoreDatabaseTestBase
{
private readonly CoreDatabaseFixture _database;

public RateSubmissionTest(CoreDatabaseFixture database)
{
_database = database;
}
public RateSubmissionTest(CoreDatabaseFixture database) : base(database) { }

[Fact]
public async Task UpdateSubmission_Should_NoThrow()
{
Submission first = await _database.PersistenceContext.GetSubmissionAsync(SubmissionStateKind.Active);
Submission first = await PersistenceContext.GetSubmissionAsync(SubmissionStateKind.Active);

first.Rate(new Fraction(0.5), Points.None);
first.State.Kind.Should().Be(SubmissionStateKind.Completed);

first.UpdatePoints(new Fraction(0.5), Points.None);
first.State.Kind.Should().Be(SubmissionStateKind.Completed);
}

public Task DisposeAsync()
{
return _database.ResetAsync();
}
}
45 changes: 45 additions & 0 deletions tests/ITMO.Dev.ASAP.Tests.Core/CoreDatabaseTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using ITMO.Dev.ASAP.Application.DataAccess;
using ITMO.Dev.ASAP.DataAccess.Contexts;
using ITMO.Dev.ASAP.Seeding.Extensions;
using ITMO.Dev.ASAP.Tests.Core.Fixtures;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Xunit.Abstractions;

namespace ITMO.Dev.ASAP.Tests.Core;

public class CoreDatabaseTestBase : CoreTestBase, IAsyncLifetime
{
private AsyncServiceScope _scope;

protected CoreDatabaseTestBase(CoreDatabaseFixture fixture, ITestOutputHelper? output = null) : base(output)
{
Fixture = fixture;
}

protected DatabaseContext Context { get; private set; } = null!;

protected IPersistenceContext PersistenceContext { get; private set; } = null!;

protected CoreDatabaseFixture Fixture { get; }

public async Task InitializeAsync()
{
_scope = Fixture.CreateAsyncScope();

await _scope.UseDatabaseSeeders<TestDatabaseContext>();
Context = _scope.ServiceProvider.GetRequiredService<TestDatabaseContext>();
PersistenceContext = _scope.ServiceProvider.GetRequiredService<IPersistenceContext>();
}

public async Task DisposeAsync()
{
await Fixture.ResetAsync();
await _scope.DisposeAsync();
}

protected T GetRequiredService<T>() where T : class
{
return _scope.ServiceProvider.GetRequiredService<T>();
}
}
58 changes: 18 additions & 40 deletions tests/ITMO.Dev.ASAP.Tests.Core/Fixtures/CoreDatabaseFixture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using ITMO.Dev.ASAP.Application.DataAccess;
using ITMO.Dev.ASAP.Application.Extensions;
using ITMO.Dev.ASAP.DataAccess.Contexts;
using ITMO.Dev.ASAP.DataAccess.Extensions;
using ITMO.Dev.ASAP.DataAccess.Models;
using ITMO.Dev.ASAP.DataAccess.Models.Users;
Expand All @@ -9,25 +7,37 @@
using ITMO.Dev.ASAP.Tests.Fixtures;
using ITMO.Dev.ASAP.Tests.Tools;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Respawn;
using Respawn.Graph;
using Serilog;
using System.Data.Common;

namespace ITMO.Dev.ASAP.Tests.Core.Fixtures;

public class CoreDatabaseFixture : DatabaseFixture
{
public AsyncServiceScope CreateAsyncScope()
{
return Provider.CreateAsyncScope();
}

protected override void ConfigureServices(IServiceCollection collection)
{
collection.AddDatabaseContext((p, x) => x
collection.AddDatabaseContext(x => x
.UseNpgsql(Container.GetConnectionString() + ";Include Error Detail = true;")
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
.UseLoggerFactory(LoggerFactory.Create(b => b.AddSerilog(new StaticLogger()).AddConsole())));

collection.AddDbContext<TestDatabaseContext>(builder => builder
.UseNpgsql(Container.GetConnectionString() + ";Include Error Detail = true;")
.UseLazyLoadingProxies()
.UseNpgsql(Container.GetConnectionString())
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
.UseLoggerFactory(LoggerFactory.Create(b => b.AddSerilog(new StaticLogger()))));
.UseLoggerFactory(LoggerFactory.Create(b => b.AddSerilog(new StaticLogger()).AddConsole()))
.ConfigureWarnings(x => x.Ignore(CoreEventId.NavigationBaseIncludeIgnored)));

collection.AddApplicationConfiguration();

Expand All @@ -44,39 +54,12 @@ protected override void ConfigureServices(IServiceCollection collection)
collection.AddDatabaseSeeders();
}

public DatabaseContext Context { get; private set; } = null!;

public IPersistenceContext PersistenceContext { get; private set; } = null!;

public AsyncServiceScope Scope { get; private set; }

public override async Task DisposeAsync()
{
await base.DisposeAsync();
await Scope.DisposeAsync();
}

public override async Task ResetAsync()
{
await base.ResetAsync();
Context.ChangeTracker.Clear();
await Scope.UseDatabaseSeeders();
}

protected virtual void ConfigureSeeding(EntityGenerationOptions options) { }

protected override async ValueTask UseProviderAsync(IServiceProvider provider)
{
Scope = provider.CreateAsyncScope();

// Caching to local variable to avoid redundant boxing
IServiceScope scope = Scope;

await scope.UseDatabaseContext();
await scope.UseDatabaseSeeders();

Context = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
PersistenceContext = scope.ServiceProvider.GetRequiredService<IPersistenceContext>();
await using AsyncServiceScope asyncScope = provider.CreateAsyncScope();
await asyncScope.UseDatabaseContext();
}

protected override RespawnerOptions GetRespawnOptions()
Expand All @@ -88,9 +71,4 @@ protected override RespawnerOptions GetRespawnOptions()
TablesToIgnore = new[] { new Table("__EFMigrationsHistory") },
};
}

protected override DbConnection CreateConnection()
{
return Context.Database.GetDbConnection();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ITMO.Dev.ASAP.DataAccess.Contexts;
using Microsoft.EntityFrameworkCore;

namespace ITMO.Dev.ASAP.Tests.Core.Fixtures;

public class TestDatabaseContext : DatabaseContext
{
public TestDatabaseContext(DbContextOptions<TestDatabaseContext> options) : base(options) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,21 @@
namespace ITMO.Dev.ASAP.Tests.Core.Handlers.Assignments;

[Collection(nameof(CoreDatabaseCollectionFixture))]
public class GetAssignmentsBySubjectCourseTests : CoreTestBase
public class GetAssignmentsBySubjectCourseTests : CoreDatabaseTestBase
{
private readonly CoreDatabaseFixture _database;

public GetAssignmentsBySubjectCourseTests(CoreDatabaseFixture database)
{
_database = database;
}
public GetAssignmentsBySubjectCourseTests(CoreDatabaseFixture database) : base(database) { }

[Fact]
public async Task HandleAsync_ShouldReturnCorrectNumberOfAssignments()
{
// Arrange
SubjectCourseModel subjectCourse = await _database.Context.SubjectCourses
SubjectCourseModel subjectCourse = await Context.SubjectCourses
.OrderBy(x => x.Id)
.Where(x => x.Assignments.Count != 0)
.FirstAsync();

var query = new GetAssignmentsBySubjectCourse.Query(subjectCourse.Id);
var handler = new GetAssignmentsBySubjectCourseHandler(_database.PersistenceContext);
var handler = new GetAssignmentsBySubjectCourseHandler(PersistenceContext);

// Act
GetAssignmentsBySubjectCourse.Response response = await handler.Handle(query, default);
Expand Down
Loading

0 comments on commit 1c4c1bd

Please sign in to comment.