Skip to content

Commit d914655

Browse files
committed
Add standalone integration test for MongoDB repositories
Signed-off-by: Victor Chang <[email protected]>
1 parent ff677ce commit d914655

19 files changed

+3127
-27
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ jobs:
160160

161161
unit-test:
162162
runs-on: ubuntu-latest
163+
services:
164+
mongo:
165+
image: mongo
166+
env:
167+
MONGO_INITDB_ROOT_USERNAME: root
168+
MONGO_INITDB_ROOT_PASSWORD: rootpassword
169+
ports:
170+
- 27017:27017
163171
steps:
164172
- name: Set up JDK 11
165173
uses: actions/setup-java@v3

src/Database/Api/Repositories/IPayloadRepository.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public interface IPayloadRepository
2929

3030
Task<Payload> RemoveAsync(Payload entity, CancellationToken cancellationToken = default);
3131

32-
Task<bool> ContainsAsync(Expression<Func<Payload, bool>> predicate, CancellationToken cancellationToken = default);
33-
3432
Task<int> RemovePendingPayloadsAsync(CancellationToken cancellationToken = default);
3533

3634
Task<List<Payload>> GetPayloadsInStateAsync(CancellationToken cancellationToken = default, params Payload.PayloadState[] states);

src/Database/EntityFramework/Test/StorageMetadataWrapperTest.cs renamed to src/Database/Api/Test/StorageMetadataWrapperTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616

1717
using Monai.Deploy.InformaticsGateway.Api.Rest;
1818
using Monai.Deploy.InformaticsGateway.Api.Storage;
19-
using Monai.Deploy.InformaticsGateway.Database.Api;
2019

21-
namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test
20+
namespace Monai.Deploy.InformaticsGateway.Database.Api.Test
2221
{
2322
public class StorageMetadataWrapperTest
2423
{

src/Database/EntityFramework/Repositories/PayloadRepository.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
using System.Linq.Expressions;
1817
using Ardalis.GuardClauses;
1918
using Microsoft.EntityFrameworkCore;
2019
using Microsoft.Extensions.DependencyInjection;
@@ -68,14 +67,6 @@ public async Task<Payload> AddAsync(Payload item, CancellationToken cancellation
6867
}).ConfigureAwait(false);
6968
}
7069

71-
public async Task<bool> ContainsAsync(Expression<Func<Payload, bool>> predicate, CancellationToken cancellationToken = default)
72-
{
73-
return await _retryPolicy.ExecuteAsync(async () =>
74-
{
75-
return await _dataset.AnyAsync(predicate, cancellationToken).ConfigureAwait(false);
76-
}).ConfigureAwait(false);
77-
}
78-
7970
public async Task<Payload> RemoveAsync(Payload entity, CancellationToken cancellationToken = default)
8071
{
8172
Guard.Against.Null(entity);
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright 2022 MONAI Consortium
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
using Microsoft.EntityFrameworkCore;
18+
using Microsoft.Extensions.DependencyInjection;
19+
using Microsoft.Extensions.Logging;
20+
using Microsoft.Extensions.Options;
21+
using Monai.Deploy.InformaticsGateway.Api.Storage;
22+
using Monai.Deploy.InformaticsGateway.Configuration;
23+
using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories;
24+
using Moq;
25+
26+
namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test
27+
{
28+
[Collection("SqliteDatabase")]
29+
public class PayloadRepositoryTest
30+
{
31+
private readonly SqliteDatabaseFixture _databaseFixture;
32+
33+
private readonly Mock<IServiceScopeFactory> _serviceScopeFactory;
34+
private readonly Mock<ILogger<PayloadRepository>> _logger;
35+
private readonly IOptions<InformaticsGatewayConfiguration> _options;
36+
37+
private readonly Mock<IServiceScope> _serviceScope;
38+
private readonly IServiceProvider _serviceProvider;
39+
40+
public PayloadRepositoryTest(SqliteDatabaseFixture databaseFixture)
41+
{
42+
_databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture));
43+
44+
_serviceScopeFactory = new Mock<IServiceScopeFactory>();
45+
_logger = new Mock<ILogger<PayloadRepository>>();
46+
_options = Options.Create(new InformaticsGatewayConfiguration());
47+
48+
_serviceScope = new Mock<IServiceScope>();
49+
var services = new ServiceCollection();
50+
services.AddScoped(p => _logger.Object);
51+
services.AddScoped(p => databaseFixture.DatabaseContext);
52+
53+
_serviceProvider = services.BuildServiceProvider();
54+
_serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object);
55+
_serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider);
56+
57+
_options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 };
58+
_logger.Setup(p => p.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
59+
}
60+
61+
[Fact]
62+
public async Task GivenAPayload_WhenAddingToDatabase_ExpectItToBeSaved()
63+
{
64+
var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5);
65+
payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()));
66+
payload.State = Payload.PayloadState.Move;
67+
68+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
69+
await store.AddAsync(payload).ConfigureAwait(false);
70+
var actual = await _databaseFixture.DatabaseContext.Set<Payload>().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(false);
71+
72+
Assert.NotNull(actual);
73+
Assert.Equal(payload.Key, actual!.Key);
74+
Assert.Equal(payload.State, actual!.State);
75+
Assert.Equal(payload.Count, actual!.Count);
76+
Assert.Equal(payload.RetryCount, actual!.RetryCount);
77+
Assert.Equal(payload.CorrelationId, actual!.CorrelationId);
78+
Assert.Equal(payload.CalledAeTitle, actual!.CalledAeTitle);
79+
Assert.Equal(payload.CallingAeTitle, actual!.CallingAeTitle);
80+
Assert.Equal(payload.Timeout, actual!.Timeout);
81+
Assert.Equal(payload.Files, actual!.Files);
82+
}
83+
84+
[Fact]
85+
public async Task GivenAPayload_WhenRemoveIsCalled_ExpectItToDeleted()
86+
{
87+
var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5);
88+
payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()));
89+
payload.State = Payload.PayloadState.Move;
90+
91+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
92+
var added = await store.AddAsync(payload).ConfigureAwait(false);
93+
94+
var removed = await store.RemoveAsync(added!).ConfigureAwait(false);
95+
Assert.Same(removed, added);
96+
97+
var dbResult = await _databaseFixture.DatabaseContext.Set<Payload>().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(false);
98+
Assert.Null(dbResult);
99+
}
100+
101+
[Fact]
102+
public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned()
103+
{
104+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
105+
106+
var expected = await _databaseFixture.DatabaseContext.Set<Payload>().ToListAsync().ConfigureAwait(false);
107+
var actual = await store.ToListAsync().ConfigureAwait(false);
108+
109+
Assert.Equal(expected, actual);
110+
}
111+
112+
[Fact]
113+
public async Task GivenAPayload_WhenUpdateIsCalled_ExpectItToSaved()
114+
{
115+
var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5);
116+
payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()));
117+
118+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
119+
var added = await store.AddAsync(payload).ConfigureAwait(false);
120+
121+
added.State = Payload.PayloadState.Notify;
122+
added.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()));
123+
var updated = await store.UpdateAsync(payload).ConfigureAwait(false);
124+
Assert.NotNull(updated);
125+
126+
var actual = await _databaseFixture.DatabaseContext.Set<Payload>().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(false);
127+
128+
Assert.NotNull(actual);
129+
Assert.Equal(updated.Key, actual!.Key);
130+
Assert.Equal(updated.State, actual!.State);
131+
Assert.Equal(updated.Count, actual!.Count);
132+
Assert.Equal(updated.RetryCount, actual!.RetryCount);
133+
Assert.Equal(updated.CorrelationId, actual!.CorrelationId);
134+
Assert.Equal(updated.CalledAeTitle, actual!.CalledAeTitle);
135+
Assert.Equal(updated.CallingAeTitle, actual!.CallingAeTitle);
136+
Assert.Equal(updated.Timeout, actual!.Timeout);
137+
Assert.Equal(updated.Files, actual!.Files);
138+
}
139+
140+
[Fact]
141+
public async Task GivenPayloadsInDifferentStates_WhenRemovePendingPayloadsAsyncIsCalled_ExpectPendingPayloadsToBeRemoved()
142+
{
143+
var set = _databaseFixture.DatabaseContext.Set<Payload>();
144+
set.RemoveRange(set.ToList());
145+
await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(false);
146+
147+
var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Created };
148+
var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Created };
149+
var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Move };
150+
var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Notify };
151+
var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Notify };
152+
153+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
154+
_ = await store.AddAsync(payload1).ConfigureAwait(false);
155+
_ = await store.AddAsync(payload2).ConfigureAwait(false);
156+
_ = await store.AddAsync(payload3).ConfigureAwait(false);
157+
_ = await store.AddAsync(payload4).ConfigureAwait(false);
158+
_ = await store.AddAsync(payload5).ConfigureAwait(false);
159+
160+
var result = await store.RemovePendingPayloadsAsync().ConfigureAwait(false);
161+
Assert.Equal(2, result);
162+
163+
var actual = await set.ToListAsync().ConfigureAwait(false);
164+
Assert.Equal(3, actual.Count);
165+
166+
foreach (var payload in actual)
167+
{
168+
Assert.NotEqual(Payload.PayloadState.Created, payload.State);
169+
}
170+
}
171+
172+
[Fact]
173+
public async Task GivenPayloadsInDifferentStates_WhenGetPayloadsInStateAsyncIsCalled_ExpectMatchingPayloadsToBeReturned()
174+
{
175+
var set = _databaseFixture.DatabaseContext.Set<Payload>();
176+
set.RemoveRange(set.ToList());
177+
await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(false);
178+
179+
var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Created };
180+
var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Created };
181+
var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Move };
182+
var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Notify };
183+
var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5) { State = Payload.PayloadState.Notify };
184+
185+
var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options);
186+
_ = await store.AddAsync(payload1).ConfigureAwait(false);
187+
_ = await store.AddAsync(payload2).ConfigureAwait(false);
188+
_ = await store.AddAsync(payload3).ConfigureAwait(false);
189+
_ = await store.AddAsync(payload4).ConfigureAwait(false);
190+
_ = await store.AddAsync(payload5).ConfigureAwait(false);
191+
192+
var result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Move).ConfigureAwait(false);
193+
Assert.Single(result);
194+
195+
result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Created).ConfigureAwait(false);
196+
Assert.Equal(2, result.Count);
197+
198+
result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify).ConfigureAwait(false);
199+
Assert.Equal(2, result.Count);
200+
201+
result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify, Payload.PayloadState.Created).ConfigureAwait(false);
202+
Assert.Equal(4, result.Count);
203+
}
204+
}
205+
}

src/Database/EntityFramework/Test/SqliteDatabaseFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using Microsoft.EntityFrameworkCore;
1818
using Monai.Deploy.InformaticsGateway.Api;
19+
using Monai.Deploy.InformaticsGateway.Api.Storage;
1920

2021
namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test
2122
{

0 commit comments

Comments
 (0)