Skip to content

Commit

Permalink
Cleanup test dicom instances after tests complete (#1254)
Browse files Browse the repository at this point in the history
Cleanup test dicom instances after tests complete
  • Loading branch information
pengchen0692 authored Jan 5, 2022
1 parent 5b5a5f5 commit de06ccc
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 120 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using Dicom;
using EnsureThat;
using Microsoft.Health.Dicom.Core.Extensions;
using Microsoft.Health.Dicom.Core.Features.Model;

namespace Microsoft.Health.Dicom.Web.Tests.E2E.Common
{
internal class DicomInstanceId
{
private const StringComparison EqualsStringComparison = StringComparison.Ordinal;
public DicomInstanceId(string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid, string partitionName)
{
StudyInstanceUid = EnsureArg.IsNotNullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid));
SeriesInstanceUid = EnsureArg.IsNotNullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid));
SopInstanceUid = EnsureArg.IsNotNullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid));
PartitionName = partitionName;
}

public string StudyInstanceUid { get; }
public string SeriesInstanceUid { get; }
public string SopInstanceUid { get; }
public string PartitionName { get; }

public static DicomInstanceId FromDicomFile(DicomFile dicomFile, string partitionName = default)
{
InstanceIdentifier instanceIdentifier = dicomFile.Dataset.ToInstanceIdentifier();
return new DicomInstanceId(instanceIdentifier.StudyInstanceUid, instanceIdentifier.SeriesInstanceUid, instanceIdentifier.SopInstanceUid, partitionName);
}

public override bool Equals(object obj)
{
if (obj is DicomInstanceId instanceId)
{
return StudyInstanceUid.Equals(instanceId.StudyInstanceUid, EqualsStringComparison) &&
SeriesInstanceUid.Equals(instanceId.SeriesInstanceUid, EqualsStringComparison) &&
SopInstanceUid.Equals(instanceId.SopInstanceUid, EqualsStringComparison) &&
PartitionName.Equals(instanceId.PartitionName, EqualsStringComparison);
}

return false;
}

public override int GetHashCode() => HashCode.Combine(StudyInstanceUid, SeriesInstanceUid, SopInstanceUid, PartitionName);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@
// -------------------------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Dicom;
using EnsureThat;
using Microsoft.Health.Dicom.Client;
using Microsoft.Health.Dicom.Core.Extensions;
using Microsoft.Health.Dicom.Core.Features.Model;

namespace Microsoft.Health.Dicom.Web.Tests.E2E.Common
{
internal class DicomInstancesManager : IAsyncDisposable
{

private readonly IDicomWebClient _dicomWebClient;
private readonly HashSet<InstanceIdentifier> _instanceIds;
private readonly ConcurrentBag<DicomInstanceId> _instanceIds;

public DicomInstancesManager(IDicomWebClient dicomWebClient)
{
_dicomWebClient = EnsureArg.IsNotNull(dicomWebClient, nameof(dicomWebClient));
_instanceIds = new HashSet<InstanceIdentifier>();
_instanceIds = new ConcurrentBag<DicomInstanceId>();
}

public async ValueTask DisposeAsync()
Expand All @@ -32,7 +34,7 @@ public async ValueTask DisposeAsync()
{
try
{
await _dicomWebClient.DeleteInstanceAsync(id.StudyInstanceUid, id.SeriesInstanceUid, id.SopInstanceUid);
await _dicomWebClient.DeleteInstanceAsync(id.StudyInstanceUid, id.SeriesInstanceUid, id.SopInstanceUid, id.PartitionName);
}
catch (DicomWebException)
{
Expand All @@ -41,12 +43,44 @@ public async ValueTask DisposeAsync()
}
}

public async Task StoreAsync(DicomFile dicomFile, CancellationToken cancellationToken = default)
public async Task<DicomWebResponse<DicomDataset>> StoreAsync(DicomFile dicomFile, string studyInstanceUid = default, string partitionName = default, CancellationToken cancellationToken = default)
{
EnsureArg.IsNotNull(dicomFile, nameof(dicomFile));
_instanceIds.Add(dicomFile.Dataset.ToInstanceIdentifier());
await _dicomWebClient.StoreAsync(dicomFile, cancellationToken: cancellationToken);
_instanceIds.Add(DicomInstanceId.FromDicomFile(dicomFile, partitionName));
return await _dicomWebClient.StoreAsync(dicomFile, studyInstanceUid, partitionName, cancellationToken);
}

public async Task<DicomWebResponse<DicomDataset>> StoreAsync(HttpContent content, string partitionName = default, CancellationToken cancellationToken = default, DicomInstanceId instanceId = default)
{
EnsureArg.IsNotNull(content, nameof(content));
// Null instanceId indiates Store will fail
if (instanceId != null)
{
_instanceIds.Add(instanceId);
}
return await _dicomWebClient.StoreAsync(content, partitionName, cancellationToken);
}

public async Task<DicomWebResponse<DicomDataset>> StoreAsync(IEnumerable<DicomFile> dicomFiles, string studyInstanceUid = default, string partitionName = default, CancellationToken cancellationToken = default)
{
EnsureArg.IsNotNull(dicomFiles, nameof(dicomFiles));
foreach (var file in dicomFiles)
{
_instanceIds.Add(DicomInstanceId.FromDicomFile(file, partitionName));
}

return await _dicomWebClient.StoreAsync(dicomFiles, studyInstanceUid, partitionName, cancellationToken);
}

public async Task<DicomWebResponse<DicomDataset>> StoreAsync(Stream stream, string studyInstanceUid = default, string partitionName = default, CancellationToken cancellationToken = default, DicomInstanceId instanceId = default)
{
EnsureArg.IsNotNull(stream, nameof(stream));
// Null instanceId indiates Store will fail
if (instanceId != null)
{
_instanceIds.Add(instanceId);
}
return await _dicomWebClient.StoreAsync(stream, studyInstanceUid, partitionName, cancellationToken);
}
}
}
22 changes: 16 additions & 6 deletions test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/Audit/AuditTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,28 @@
using Microsoft.Health.Dicom.Core.Features.Audit;
using Microsoft.Health.Dicom.Core.Features.Model;
using Microsoft.Health.Dicom.Tests.Common;
using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
using Xunit;

namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest.Audit
{
/// <summary>
/// Provides Audit specific tests.
/// </summary>
public class AuditTests : IClassFixture<AuditTestFixture>
public class AuditTests : IClassFixture<AuditTestFixture>, IAsyncLifetime
{
private readonly AuditTestFixture _fixture;
private readonly IDicomWebClient _client;
private readonly DicomInstancesManager _instancesManager;

private readonly TraceAuditLogger _auditLogger;

public AuditTests(AuditTestFixture fixture)
{
EnsureArg.IsNotNull(fixture, nameof(fixture));
_fixture = fixture;
_client = fixture.Client;

_client = fixture.GetDicomWebClient();
_instancesManager = new DicomInstancesManager(_client);
_auditLogger = _fixture.AuditLogger;
}

Expand Down Expand Up @@ -78,7 +80,7 @@ public async Task GivenRetrieveRequestForFrame_WhenResourceIsFound_ThenAuditLogE
{
DicomFile dicomFile = Samples.CreateRandomDicomFileWithPixelData(frames: 1);
var dicomInstance = dicomFile.Dataset.ToInstanceIdentifier();
await _client.StoreAsync(new[] { dicomFile }, dicomInstance.StudyInstanceUid);
await _instancesManager.StoreAsync(new[] { dicomFile }, dicomInstance.StudyInstanceUid);

await ExecuteAndValidate(
() => _client.RetrieveFramesAsync(dicomInstance.StudyInstanceUid, dicomInstance.SeriesInstanceUid, dicomInstance.SopInstanceUid, frames: new int[] { 1 }),
Expand Down Expand Up @@ -189,12 +191,19 @@ public async Task GivenStoreRequest_WhenStoringUsingStudyInstanceUid_ThenAuditLo
InstanceIdentifier dicomInstance = dicomFile.Dataset.ToInstanceIdentifier();

await ExecuteAndValidate(
() => _client.StoreAsync(new[] { dicomFile }, studyInstanceUid),
() => _instancesManager.StoreAsync(new[] { dicomFile }, studyInstanceUid),
AuditEventSubType.Store,
$"studies/{dicomInstance.StudyInstanceUid}",
HttpStatusCode.OK);
}

public Task InitializeAsync() => Task.CompletedTask;

public async Task DisposeAsync()
{
await _instancesManager.DisposeAsync();
}

private async Task ExecuteAndValidate<T>(Func<Task<T>> action, string expectedAction, string expectedPathSegment, HttpStatusCode expectedStatusCode)
{
if (!_fixture.IsInProcess)
Expand Down Expand Up @@ -237,9 +246,10 @@ private async Task<InstanceIdentifier> CreateDicomFileAndGetInstanceIdentifierAs
string studyInstanceUid = TestUidGenerator.Generate();
DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUid);
InstanceIdentifier dicomInstance = dicomFile.Dataset.ToInstanceIdentifier();
await _client.StoreAsync(new[] { dicomFile }, studyInstanceUid);
await _instancesManager.StoreAsync(new[] { dicomFile }, studyInstanceUid);

return dicomInstance;
}

}
}
16 changes: 13 additions & 3 deletions test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ChangeFeedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,24 @@
using Microsoft.Health.Dicom.Client;
using Microsoft.Health.Dicom.Client.Models;
using Microsoft.Health.Dicom.Tests.Common;
using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
using Xunit;
using ChangeFeedAction = Microsoft.Health.Dicom.Client.Models.ChangeFeedAction;
using ChangeFeedState = Microsoft.Health.Dicom.Client.Models.ChangeFeedState;

namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest
{
[CollectionDefinition("Non-Parallel Collection", DisableParallelization = true)]
public class ChangeFeedTests : IClassFixture<HttpIntegrationTestFixture<Startup>>
public class ChangeFeedTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
{
private readonly IDicomWebClient _client;
private readonly DicomInstancesManager _instancesManager;

public ChangeFeedTests(HttpIntegrationTestFixture<Startup> fixture)
{
EnsureArg.IsNotNull(fixture, nameof(fixture));
_client = fixture.Client;
_client = fixture.GetDicomWebClient();
_instancesManager = new DicomInstancesManager(_client);
}

[Fact]
Expand Down Expand Up @@ -200,10 +203,17 @@ public async Task GivenAnInvalidParameter_WhenRetrievingChangeFeedLatest_ThenBad
Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode);
}

public Task InitializeAsync() => Task.CompletedTask;

public async Task DisposeAsync()
{
await _instancesManager.DisposeAsync();
}

private async Task CreateFile(string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid)
{
DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
await _client.StoreAsync(new[] { dicomFile1 }, studyInstanceUid);
await _instancesManager.StoreAsync(new[] { dicomFile1 }, studyInstanceUid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@
using Microsoft.Health.Dicom.Client.Models;
using Microsoft.Health.Dicom.Core.Features.Query;
using Microsoft.Health.Dicom.Tests.Common;
using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
using Xunit;

namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest
{
public class DataPartitionEnabledTests : IClassFixture<DataPartitionEnabledHttpIntegrationTestFixture<Startup>>
public class DataPartitionEnabledTests : IClassFixture<DataPartitionEnabledHttpIntegrationTestFixture<Startup>>, IAsyncLifetime
{
private readonly IDicomWebClient _client;
private readonly DicomInstancesManager _instancesManager;

public DataPartitionEnabledTests(DataPartitionEnabledHttpIntegrationTestFixture<Startup> fixture)
{
EnsureArg.IsNotNull(fixture, nameof(fixture));
_client = fixture.Client;
_client = fixture.GetDicomWebClient();
_instancesManager = new DicomInstancesManager(_client);
}

[Fact]
Expand All @@ -35,8 +38,8 @@ public async Task WhenRetrievingPartitions_TheServerShouldReturnAllPartitions()

DicomFile dicomFile = Samples.CreateRandomDicomFile();

using DicomWebResponse<DicomDataset> response1 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);
using DicomWebResponse<DicomDataset> response1 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);

using DicomWebResponse<IEnumerable<PartitionEntry>> response3 = await _client.GetPartitionsAsync();
Assert.True(response3.IsSuccessStatusCode);
Expand All @@ -56,7 +59,7 @@ public async Task GivenDatasetWithNewPartitionName_WhenStoring_TheServerShouldRe

DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUID);

using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition);
using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition);

Assert.True(response.IsSuccessStatusCode);

Expand All @@ -73,7 +76,7 @@ public async Task GivenDatasetWithNewPartitionName_WhenStoringWithStudyUid_TheSe
var studyInstanceUID = TestUidGenerator.Generate();
DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUid: studyInstanceUID);

using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(dicomFile, studyInstanceUID, newPartition);
using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(dicomFile, studyInstanceUID, newPartition);

Assert.True(response.IsSuccessStatusCode);

Expand All @@ -94,8 +97,8 @@ public async Task WhenRetrievingWithPartitionName_TheServerShouldReturnOnlyTheSp

DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUID, seriesInstanceUID, sopInstanceUID);

using DicomWebResponse<DicomDataset> response1 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);
using DicomWebResponse<DicomDataset> response1 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);

using DicomWebResponse<DicomFile> response3 = await _client.RetrieveInstanceAsync(studyInstanceUID, seriesInstanceUID, sopInstanceUID, partitionName: newPartition1);
Assert.True(response3.IsSuccessStatusCode);
Expand All @@ -116,8 +119,8 @@ public async Task GivenDatasetInstancesWithDifferentPartitions_WhenDeleted_OneDe

DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUID, seriesInstanceUID, sopInstanceUID);

using DicomWebResponse<DicomDataset> response1 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);
using DicomWebResponse<DicomDataset> response1 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition2);

using DicomWebResponse response3 = await _client.DeleteInstanceAsync(studyInstanceUID, seriesInstanceUID, sopInstanceUID, newPartition1);
Assert.True(response3.IsSuccessStatusCode);
Expand Down Expand Up @@ -148,8 +151,8 @@ public async Task GivenMatchingStudiesInDifferentPartitions_WhenSearchForStudySe
{ DicomTag.Modality, "MRI" },
});

using DicomWebResponse<DicomDataset> response1 = await _client.StoreAsync(new[] { file1 }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _client.StoreAsync(new[] { file2 }, partitionName: newPartition2);
using DicomWebResponse<DicomDataset> response1 = await _instancesManager.StoreAsync(new[] { file1 }, partitionName: newPartition1);
using DicomWebResponse<DicomDataset> response2 = await _instancesManager.StoreAsync(new[] { file2 }, partitionName: newPartition2);

using DicomWebAsyncEnumerableResponse<DicomDataset> response = await _client.QueryStudySeriesAsync(studyUid, "Modality=MRI", newPartition1);

Expand All @@ -168,7 +171,7 @@ public async Task GivenAnInstance_WhenRetrievingChangeFeedWithPartition_ThenPart
string sopInstanceUID = TestUidGenerator.Generate();

DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUID, seriesInstanceUID, sopInstanceUID);
using DicomWebResponse<DicomDataset> response1 = await _client.StoreAsync(new[] { dicomFile }, partitionName: newPartition);
using DicomWebResponse<DicomDataset> response1 = await _instancesManager.StoreAsync(new[] { dicomFile }, partitionName: newPartition);
Assert.True(response1.IsSuccessStatusCode);

long initialSequence;
Expand Down Expand Up @@ -196,6 +199,13 @@ public async Task GivenAnInstance_WhenRetrievingChangeFeedWithPartition_ThenPart
}
}

public Task InitializeAsync() => Task.CompletedTask;

public async Task DisposeAsync()
{
await _instancesManager.DisposeAsync();
}

private (string SopInstanceUid, string RetrieveUri, string SopClassUid) ConvertToReferencedSopSequenceEntry(DicomDataset dicomDataset, string partitionName)
{
string studyInstanceUid = dicomDataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
Expand Down
Loading

0 comments on commit de06ccc

Please sign in to comment.