From de06cccbc0bf1296aba9f846717d73f34bd79db5 Mon Sep 17 00:00:00 2001
From: Peng Chen <67345894+pengchen0692@users.noreply.github.com>
Date: Wed, 5 Jan 2022 10:48:39 -0800
Subject: [PATCH] Cleanup test dicom instances after tests complete (#1254)

Cleanup test dicom instances after tests complete
---
 .../Common/DicomInstanceId.cs                 | 52 +++++++++++++++++++
 .../Common/DicomInstancesManager.cs           | 50 +++++++++++++++---
 .../Rest/Audit/AuditTests.cs                  | 22 +++++---
 .../Rest/ChangeFeedTests.cs                   | 16 ++++--
 .../Rest/DataPartitionEnabledTests.cs         | 36 ++++++++-----
 .../Rest/DeleteTests.cs                       | 16 ++++--
 .../Rest/DicomRetrieveMetadataETagTests.cs    | 16 ++++--
 .../DicomRetrieveMetadataTransactionTests.cs  | 17 ++++--
 .../Rest/ExtendedQueryTagTests.cs             |  3 +-
 .../Rest/HttpIntegrationTestFixture.cs        |  6 ---
 .../Rest/QueryTransactionTests.cs             | 27 ++++------
 ...RetrieveTransactionResourceTests.Common.cs | 29 +++--------
 .../RetrieveTransactionResourceTests.Frame.cs |  8 +--
 ...trieveTransactionResourceTests.Instance.cs | 10 ++--
 ...RetrieveTransactionResourceTests.Series.cs | 10 ++--
 .../RetrieveTransactionResourceTests.Study.cs | 10 ++--
 .../Rest/StoreTransactionTests.cs             | 39 ++++++++------
 17 files changed, 247 insertions(+), 120 deletions(-)
 create mode 100644 test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstanceId.cs

diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstanceId.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstanceId.cs
new file mode 100644
index 0000000000..70c0812dd9
--- /dev/null
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstanceId.cs
@@ -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);
+
+    }
+}
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstancesManager.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstancesManager.cs
index 39c7330391..488007dd81 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstancesManager.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Common/DicomInstancesManager.cs
@@ -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()
@@ -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)
                 {
@@ -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);
+        }
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/Audit/AuditTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/Audit/AuditTests.cs
index c0950f9e0a..beff2091bc 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/Audit/AuditTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/Audit/AuditTests.cs
@@ -14,6 +14,7 @@
 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
@@ -21,10 +22,11 @@ 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;
 
@@ -32,8 +34,8 @@ public AuditTests(AuditTestFixture fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
             _fixture = fixture;
-            _client = fixture.Client;
-
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
             _auditLogger = _fixture.AuditLogger;
         }
 
@@ -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 }),
@@ -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)
@@ -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;
         }
+
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ChangeFeedTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ChangeFeedTests.cs
index 68b7137f3e..cfa2ac40e4 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ChangeFeedTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ChangeFeedTests.cs
@@ -11,6 +11,7 @@
 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;
@@ -18,14 +19,16 @@
 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]
@@ -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);
         }
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DataPartitionEnabledTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DataPartitionEnabledTests.cs
index c3a8e1c291..88b6e475b6 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DataPartitionEnabledTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DataPartitionEnabledTests.cs
@@ -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]
@@ -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);
@@ -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);
 
@@ -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);
 
@@ -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);
@@ -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);
@@ -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);
 
@@ -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;
@@ -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);
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DeleteTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DeleteTests.cs
index 806e2d495e..13c5ed70fe 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DeleteTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DeleteTests.cs
@@ -10,18 +10,21 @@
 using EnsureThat;
 using Microsoft.Health.Dicom.Client;
 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 DeleteTests : IClassFixture<HttpIntegrationTestFixture<Startup>>
+    public class DeleteTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
     {
         private readonly IDicomWebClient _client;
+        private readonly DicomInstancesManager _instancesManager;
 
         public DeleteTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
         }
 
         [Fact]
@@ -274,6 +277,13 @@ public async Task GivenABadSopInstanceUid_WhenDeleting_TheServerShouldReturnBack
             Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode);
         }
 
+        public Task InitializeAsync() => Task.CompletedTask;
+
+        public async Task DisposeAsync()
+        {
+            await _instancesManager.DisposeAsync();
+        }
+
         private async Task VerifyAllRemoval(string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid)
         {
             await VerifySopInstanceRemoval(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
@@ -315,7 +325,7 @@ private async Task CreateFile(string studyInstanceUid, string seriesInstanceUid,
         {
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
 
-            await _client.StoreAsync(new[] { dicomFile1 }, studyInstanceUid);
+            await _instancesManager.StoreAsync(new[] { dicomFile1 }, studyInstanceUid);
         }
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataETagTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataETagTests.cs
index 25c884f1f5..050d7b1900 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataETagTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataETagTests.cs
@@ -15,20 +15,23 @@
 using Microsoft.Health.Dicom.Core.Extensions;
 using Microsoft.Health.Dicom.Core.Messages;
 using Microsoft.Health.Dicom.Tests.Common;
+using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
 using Microsoft.Net.Http.Headers;
 using Newtonsoft.Json;
 using Xunit;
 
 namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest
 {
-    public class DicomRetrieveMetadataETagTests : IClassFixture<HttpIntegrationTestFixture<Startup>>
+    public class DicomRetrieveMetadataETagTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
     {
         private readonly IDicomWebClient _client;
+        private readonly DicomInstancesManager _instancesManager;
 
         public DicomRetrieveMetadataETagTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
         }
 
         [Fact]
@@ -319,6 +322,13 @@ public async Task GivenRetrieveInstanceMetadataRequest_WhenIfNoneMatchIsNotPrese
             ValidateResponseMetadataDataset(storedInstance, datasets[0]);
         }
 
+        public Task InitializeAsync() => Task.CompletedTask;
+
+        public async Task DisposeAsync()
+        {
+            await _instancesManager.DisposeAsync();
+        }
+
         private string GetEtagFromResponse(DicomWebAsyncEnumerableResponse<DicomDataset> response)
         {
             string eTag = null;
@@ -356,7 +366,7 @@ private async Task<DicomDataset> PostDicomFileAsync(ResourceType resourceType, s
                 dicomFile.Dataset.AddOrUpdate(dataSet);
             }
 
-            using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(new[] { dicomFile });
+            using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(new[] { dicomFile });
 
             return dicomFile.Dataset;
         }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataTransactionTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataTransactionTests.cs
index f1d600a5fb..38a62de168 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataTransactionTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/DicomRetrieveMetadataTransactionTests.cs
@@ -14,19 +14,22 @@
 using Microsoft.Health.Dicom.Core.Extensions;
 using Microsoft.Health.Dicom.Core.Messages;
 using Microsoft.Health.Dicom.Tests.Common;
+using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
 using Newtonsoft.Json;
 using Xunit;
 
 namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest
 {
-    public class DicomRetrieveMetadataTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>
+    public class DicomRetrieveMetadataTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
     {
         private readonly IDicomWebClient _client;
+        private readonly DicomInstancesManager _instancesManager;
 
         public DicomRetrieveMetadataTransactionTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
         }
 
         [Theory]
@@ -220,12 +223,20 @@ private async Task<DicomDataset> PostDicomFileAsync(ResourceType resourceType, s
                 dicomFile.Dataset.AddOrUpdate(dataSet);
             }
 
-            using (DicomWebResponse<DicomDataset> response = await _client.StoreAsync(new[] { dicomFile }))
+            using (DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(new[] { dicomFile }))
             {
                 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
             }
 
             return dicomFile.Dataset;
         }
+
+        public Task InitializeAsync()
+           => Task.CompletedTask;
+
+        public async Task DisposeAsync()
+        {
+            await _instancesManager.DisposeAsync();
+        }
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ExtendedQueryTagTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ExtendedQueryTagTests.cs
index e51e39b054..bfe794f958 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ExtendedQueryTagTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/ExtendedQueryTagTests.cs
@@ -30,8 +30,7 @@ public class ExtendedQueryTagTests : IClassFixture<WebJobsIntegrationTestFixture
         public ExtendedQueryTagTests(WebJobsIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            EnsureArg.IsNotNull(fixture.Client, nameof(fixture.Client));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
             _tagManager = new DicomTagsManager(_client);
             _instanceManager = new DicomInstancesManager(_client);
         }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/HttpIntegrationTestFixture.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/HttpIntegrationTestFixture.cs
index 0ff9812952..8227528544 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/HttpIntegrationTestFixture.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/HttpIntegrationTestFixture.cs
@@ -28,19 +28,14 @@ public HttpIntegrationTestFixture()
         protected HttpIntegrationTestFixture(string targetProjectParentDirectory, bool enableDataPartitions = false)
         {
             TestDicomWebServer = TestDicomWebServerFactory.GetTestDicomWebServer(typeof(TStartup), enableDataPartitions);
-            Client = GetDicomWebClient();
         }
 
         public bool IsInProcess => TestDicomWebServer is InProcTestDicomWebServer;
 
-        public HttpClient HttpClient => Client.HttpClient;
-
         protected TestDicomWebServer TestDicomWebServer { get; }
 
         public RecyclableMemoryStreamManager RecyclableMemoryStreamManager { get; } = new RecyclableMemoryStreamManager();
 
-        public IDicomWebClient Client { get; }
-
         public IDicomWebClient GetDicomWebClient()
         {
             return GetDicomWebClient(TestApplications.GlobalAdminServicePrincipal);
@@ -113,7 +108,6 @@ protected virtual void Dispose(bool disposing)
         {
             if (disposing)
             {
-                HttpClient.Dispose();
                 TestDicomWebServer.Dispose();
             }
         }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/QueryTransactionTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/QueryTransactionTests.cs
index 4ccc056938..ddb2683394 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/QueryTransactionTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/QueryTransactionTests.cs
@@ -4,7 +4,6 @@
 // -------------------------------------------------------------------------------------------------
 
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Text;
@@ -15,19 +14,21 @@
 using Microsoft.Health.Dicom.Core;
 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 QueryTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IDisposable
+    public class QueryTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
     {
         private readonly IDicomWebClient _client;
-        private readonly HashSet<string> _createdDicomStudies = new HashSet<string>();
+        private readonly DicomInstancesManager _instancesManager;
 
         public QueryTransactionTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
         }
 
         [Fact]
@@ -295,23 +296,15 @@ private async Task<DicomDataset> PostDicomFileAsync(DicomDataset metadataItems =
                 dicomFile1.Dataset.AddOrUpdate(metadataItems);
             }
 
-            await _client.StoreAsync(new[] { dicomFile1 });
-
-            _createdDicomStudies.Add(dicomFile1.Dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID));
-
+            await _instancesManager.StoreAsync(new[] { dicomFile1 });
             return dicomFile1.Dataset;
         }
 
-        void IDisposable.Dispose()
-        {
-            // xunit does not seem to call IAsyncDispose.DisposeAsync()
-            // Also wait should be okay in a test context
-            foreach (string studyUid in _createdDicomStudies)
-            {
-                _client.DeleteStudyAsync(studyUid).Wait();
-            }
+        public Task InitializeAsync() => Task.CompletedTask;
 
-            _createdDicomStudies.Clear();
+        public async Task DisposeAsync()
+        {
+            await _instancesManager.DisposeAsync();
         }
     }
 }
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Common.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Common.cs
index 2298e742b5..832f736442 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Common.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Common.cs
@@ -11,6 +11,7 @@
 using Microsoft.Health.Dicom.Core.Extensions;
 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
@@ -26,31 +27,23 @@ public partial class RetrieveTransactionResourceTests : IClassFixture<HttpIntegr
         private const string RequestOriginalContentTestFolder = TestFileFolder + "RequestOriginalContent";
 
         private readonly IDicomWebClient _client;
-        private readonly HashSet<string> _studiesToClean = new HashSet<string>();
+        private readonly DicomInstancesManager _instancesManager;
 
         public RetrieveTransactionResourceTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
         }
 
         private async Task<(InstanceIdentifier, DicomFile)> CreateAndStoreDicomFile(int numberOfFrames = 0)
         {
             DicomFile dicomFile = Samples.CreateRandomDicomFileWithPixelData(frames: numberOfFrames);
             var dicomInstance = dicomFile.Dataset.ToInstanceIdentifier();
-            await InternalStoreAsync(new[] { dicomFile });
+            await _instancesManager.StoreAsync(new[] { dicomFile });
             return (dicomInstance, dicomFile);
         }
 
-        private async Task InternalStoreAsync(IEnumerable<DicomFile> dicomFiles)
-        {
-            await _client.StoreAsync(dicomFiles);
-            foreach (DicomFile dicomFile in dicomFiles)
-            {
-                _studiesToClean.Add(dicomFile.Dataset.GetString(DicomTag.StudyInstanceUID));
-            }
-        }
-
         private InstanceIdentifier RandomizeInstanceIdentifier(DicomDataset dataset)
         {
             InstanceIdentifier newId = new InstanceIdentifier(TestUidGenerator.Generate(), TestUidGenerator.Generate(), TestUidGenerator.Generate());
@@ -60,19 +53,11 @@ private InstanceIdentifier RandomizeInstanceIdentifier(DicomDataset dataset)
             return newId;
         }
 
-        public Task InitializeAsync()
-        {
-            return Task.CompletedTask;
-        }
+        public Task InitializeAsync() => Task.CompletedTask;
 
         public async Task DisposeAsync()
         {
-            foreach (string studyUid in _studiesToClean)
-            {
-                await _client.DeleteStudyAsync(studyUid);
-            }
-
-            _studiesToClean.Clear();
+            await _instancesManager.DisposeAsync();
         }
 
         public static IEnumerable<object[]> GetUnsupportedAcceptHeadersForStudiesAndSeries
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Frame.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Frame.cs
index b92984290c..ccd0746f0a 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Frame.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Frame.cs
@@ -40,7 +40,7 @@ public async Task GivenSupportedAcceptHeaders_WhenRetrieveFrame_ThenServerShould
             DicomFile inputDicomFile = await DicomFile.OpenAsync(transcoderTestData.InputDicomFile);
             var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);
 
-            await InternalStoreAsync(new[] { inputDicomFile });
+            await _instancesManager.StoreAsync(new[] { inputDicomFile });
 
             DicomFile outputDicomFile = DicomFile.Open(transcoderTestData.ExpectedOutputDicomFile);
             DicomPixelData pixelData = DicomPixelData.Create(outputDicomFile.Dataset);
@@ -89,7 +89,7 @@ public async Task GivenUnsupportedTransferSyntax_WhenRetrieveFrameWithOriginalTr
                 transferSyntax: DicomTransferSyntax.HEVCH265Main10ProfileLevel51.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile });
+            await _instancesManager.StoreAsync(new[] { dicomFile });
 
             // Check for series
             using DicomWebAsyncEnumerableResponse<Stream> response = await _client.RetrieveFramesAsync(
@@ -120,7 +120,7 @@ public async Task GivenUnsupportedTransferSyntax_WhenRetrieveFrame_ThenServerSho
                 transferSyntax: DicomTransferSyntax.HEVCH265Main10ProfileLevel51.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile });
+            await _instancesManager.StoreAsync(new[] { dicomFile });
 
             DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.RetrieveFramesAsync(
                 studyInstanceUid,
@@ -141,7 +141,7 @@ public async Task GivenMultipleFrames_WhenRetrieveFrame_ThenServerShouldReturnEx
             DicomPixelData pixelData = DicomPixelData.Create(dicomFile1.Dataset);
             InstanceIdentifier dicomInstance = dicomFile1.Dataset.ToInstanceIdentifier();
 
-            await InternalStoreAsync(new[] { dicomFile1 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1 });
 
             using DicomWebAsyncEnumerableResponse<Stream> response = await _client.RetrieveFramesAsync(
                 dicomInstance.StudyInstanceUid,
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Instance.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Instance.cs
index d20515993f..0eca110d8b 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Instance.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Instance.cs
@@ -34,7 +34,7 @@ public async Task GivenSinglePartAcceptHeader_WhenRetrieveInstance_ThenServerSho
             DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
             var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);
 
-            await InternalStoreAsync(new[] { inputDicomFile });
+            await _instancesManager.StoreAsync(new[] { inputDicomFile });
 
             using DicomWebResponse<DicomFile> response = await _client.RetrieveInstanceAsync(instanceId.StudyInstanceUid, instanceId.SeriesInstanceUid, instanceId.SopInstanceUid, transferSyntax);
 
@@ -61,7 +61,7 @@ public async Task GivenMultipartAcceptHeader_WhenRetrieveInstance_ThenServerShou
             DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
             var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);
 
-            await InternalStoreAsync(new[] { inputDicomFile });
+            await _instancesManager.StoreAsync(new[] { inputDicomFile });
 
             var requestUri = new Uri(DicomApiVersions.Latest + string.Format(DicomWebConstants.BaseInstanceUriFormat, instanceId.StudyInstanceUid, instanceId.SeriesInstanceUid, instanceId.SopInstanceUid), UriKind.Relative);
 
@@ -96,7 +96,7 @@ public async Task GivenUnsupportedInternalTransferSyntax_WhenRetrieveInstance_Th
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile });
+            await _instancesManager.StoreAsync(new[] { dicomFile });
             DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.RetrieveInstanceAsync(studyInstanceUid, seriesInstanceUid, sopInstanceUid, dicomTransferSyntax: DicomTransferSyntax.ExplicitVRLittleEndian.UID.UID));
             Assert.Equal(HttpStatusCode.NotAcceptable, exception.StatusCode);
         }
@@ -116,7 +116,7 @@ public async Task GivenUnsupportedInternalTransferSyntax_WhenRetrieveInstanceWit
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile });
+            await _instancesManager.StoreAsync(new[] { dicomFile });
 
             using DicomWebResponse<DicomFile> instancesInStudy = await _client.RetrieveInstanceAsync(studyInstanceUid, seriesInstanceUid, sopInstanceUid, dicomTransferSyntax: "*");
             Assert.Equal(dicomFile.ToByteArray(), (await instancesInStudy.GetValueAsync()).ToByteArray());
@@ -136,7 +136,7 @@ public async Task GivenInstanceWithoutPixelData_WhenRetrieveInstance_ThenServerS
             var seriesInstanceUid = TestUidGenerator.Generate();
             var sopInstanceUid = TestUidGenerator.Generate();
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
-            await InternalStoreAsync(new[] { dicomFile1 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1 });
 
             using DicomWebResponse<DicomFile> instanceRetrieve = await _client.RetrieveInstanceAsync(studyInstanceUid, seriesInstanceUid, sopInstanceUid, dicomTransferSyntax: "*");
             Assert.Equal(dicomFile1.ToByteArray(), (await instanceRetrieve.GetValueAsync()).ToByteArray());
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Series.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Series.cs
index 416e3958fa..295b525385 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Series.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Series.cs
@@ -33,7 +33,7 @@ public async Task GivenSupportedAcceptHeaders_WhenRetrieveSeries_ThenServerShoul
             TranscoderTestData transcoderTestData = TranscoderTestDataHelper.GetTestData(testDataFolder);
             DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
             var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);
-            await InternalStoreAsync(new[] { inputDicomFile });
+            await _instancesManager.StoreAsync(new[] { inputDicomFile });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveSeriesAsync(instanceId.StudyInstanceUid, instanceId.SeriesInstanceUid, transferSyntax);
             Assert.Equal(DicomWebConstants.MultipartRelatedMediaType, response.ContentHeaders.ContentType.MediaType);
@@ -86,7 +86,7 @@ public async Task GivenMultipleInstances_WhenRetrieveSeries_ThenServerShouldRetu
                 TestUidGenerator.Generate(),
                 transferSyntax: DicomTransferSyntax.ExplicitVRLittleEndian.UID.UID);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2, dicomFile3 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2, dicomFile3 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveSeriesAsync(studyInstanceUid, seriesInstanceUid);
 
@@ -117,7 +117,7 @@ public async Task GivenMultipleInstancesWithMixTransferSyntax_WhenRetrieveSeries
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2 });
 
             DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.RetrieveSeriesAsync(studyInstanceUid, seriesInstanceUid, dicomTransferSyntax: DicomTransferSyntax.ExplicitVRLittleEndian.UID.UID));
             Assert.Equal(HttpStatusCode.NotAcceptable, exception.StatusCode);
@@ -140,7 +140,7 @@ public async Task GivenMultipleInstancesWithMixTransferSyntax_WhenRetrieveSeries
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveSeriesAsync(studyInstanceUid, seriesInstanceUid, dicomTransferSyntax: "*");
 
@@ -167,7 +167,7 @@ public async Task GivenInstanceWithoutPixelData_WhenRetrieveSeries_ThenServerSho
             var studyInstanceUid = TestUidGenerator.Generate();
             var seriesInstanceUid = TestUidGenerator.Generate();
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid, seriesInstanceUid);
-            await InternalStoreAsync(new[] { dicomFile1 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveSeriesAsync(studyInstanceUid, seriesInstanceUid, dicomTransferSyntax: "*");
 
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Study.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Study.cs
index 87a12ddcc7..d1557683a2 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Study.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/RetrieveTransactionResourceTests.Study.cs
@@ -34,7 +34,7 @@ public async Task GivenSupportedAcceptHeaders_WhenRetrieveStudy_ThenServerShould
             DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
             var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);
 
-            await InternalStoreAsync(new[] { inputDicomFile });
+            await _instancesManager.StoreAsync(new[] { inputDicomFile });
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveStudyAsync(instanceId.StudyInstanceUid, transferSyntax);
 
             Assert.Equal(DicomWebConstants.MultipartRelatedMediaType, response.ContentHeaders.ContentType.MediaType);
@@ -87,7 +87,7 @@ public async Task GivenMultipleInstances_WhenRetrieveStudy_ThenServerShouldRetur
                 TestUidGenerator.Generate(),
                 transferSyntax: DicomTransferSyntax.ExplicitVRLittleEndian.UID.UID);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2, dicomFile3 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2, dicomFile3 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveStudyAsync(studyInstanceUid);
 
@@ -117,7 +117,7 @@ public async Task GivenMultipleInstancesWithMixTransferSyntax_WhenRetrieveStudy_
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2 });
             DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.RetrieveStudyAsync(studyInstanceUid, dicomTransferSyntax: DicomTransferSyntax.ExplicitVRLittleEndian.UID.UID));
             Assert.Equal(HttpStatusCode.NotAcceptable, exception.StatusCode);
         }
@@ -138,7 +138,7 @@ public async Task GivenMultipleInstancesWithMixTransferSyntax_WhenRetrieveStudyW
                 transferSyntax: DicomTransferSyntax.MPEG2.UID.UID,
                 encode: false);
 
-            await InternalStoreAsync(new[] { dicomFile1, dicomFile2 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveStudyAsync(studyInstanceUid, dicomTransferSyntax: "*");
 
@@ -164,7 +164,7 @@ public async Task GivenInstanceWithoutPixelData_WhenRetrieveStudy_ThenThenServer
         {
             var studyInstanceUid = TestUidGenerator.Generate();
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid);
-            await InternalStoreAsync(new[] { dicomFile1 });
+            await _instancesManager.StoreAsync(new[] { dicomFile1 });
 
             using DicomWebAsyncEnumerableResponse<DicomFile> response = await _client.RetrieveStudyAsync(studyInstanceUid, dicomTransferSyntax: "*");
 
diff --git a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/StoreTransactionTests.cs b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/StoreTransactionTests.cs
index f37c19834a..8d3f917586 100644
--- a/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/StoreTransactionTests.cs
+++ b/test/Microsoft.Health.Dicom.Web.Tests.E2E/Rest/StoreTransactionTests.cs
@@ -14,13 +14,14 @@
 using EnsureThat;
 using Microsoft.Health.Dicom.Client;
 using Microsoft.Health.Dicom.Tests.Common;
+using Microsoft.Health.Dicom.Web.Tests.E2E.Common;
 using Microsoft.IO;
 using Microsoft.Net.Http.Headers;
 using Xunit;
 
 namespace Microsoft.Health.Dicom.Web.Tests.E2E.Rest
 {
-    public class StoreTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>
+    public class StoreTransactionTests : IClassFixture<HttpIntegrationTestFixture<Startup>>, IAsyncLifetime
     {
         private const ushort ValidationFailedFailureCode = 43264;
         private const ushort SopInstanceAlreadyExistsFailureCode = 45070;
@@ -28,11 +29,13 @@ public class StoreTransactionTests : IClassFixture<HttpIntegrationTestFixture<St
 
         private readonly IDicomWebClient _client;
         private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
+        private readonly DicomInstancesManager _instancesManager;
 
         public StoreTransactionTests(HttpIntegrationTestFixture<Startup> fixture)
         {
             EnsureArg.IsNotNull(fixture, nameof(fixture));
-            _client = fixture.Client;
+            _client = fixture.GetDicomWebClient();
+            _instancesManager = new DicomInstancesManager(_client);
             _recyclableMemoryStreamManager = fixture.RecyclableMemoryStreamManager;
         }
 
@@ -89,7 +92,7 @@ public async Task GivenAMultipartRequestWithNoContent_WhenStoring_TheServerShoul
             var multiContent = new MultipartContent("related");
             multiContent.Headers.ContentType.Parameters.Add(new System.Net.Http.Headers.NameValueHeaderValue("type", $"\"{DicomWebConstants.MediaTypeApplicationDicom.MediaType}\""));
 
-            using DicomWebResponse response = await _client.StoreAsync(multiContent);
+            using DicomWebResponse response = await _instancesManager.StoreAsync(multiContent);
 
             Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
         }
@@ -105,7 +108,7 @@ public async Task GivenAMultipartRequestWithEmptyContent_WhenStoring_TheServerSh
             multiContent.Add(byteContent);
 
             DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(
-                () => _client.StoreAsync(multiContent));
+                () => _instancesManager.StoreAsync(multiContent));
 
             Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
         }
@@ -135,7 +138,7 @@ public async Task GivenAMultipartRequestWithAnInvalidMultipartSection_WhenStorin
                     multiContent.Add(validByteContent);
                 }
 
-                using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(multiContent);
+                using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(multiContent, instanceId: DicomInstanceId.FromDicomFile(validFile));
 
                 Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
 
@@ -169,7 +172,7 @@ public async Task GivenAMultipartRequestWithTypeParameterAndFirstSectionWithoutC
                     multiContent.Add(byteContent);
                 }
 
-                using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(multiContent);
+                using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(multiContent, instanceId: DicomInstanceId.FromDicomFile(dicomFile));
 
                 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
 
@@ -191,7 +194,7 @@ public async Task GivenAllDifferentStudyInstanceUIDs_WhenStoringWithProvidedStud
 
             var studyInstanceUID = TestUidGenerator.Generate();
 
-            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.StoreAsync(
+            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _instancesManager.StoreAsync(
                 new[] { dicomFile1, dicomFile2 }, studyInstanceUid: studyInstanceUID));
 
             Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
@@ -219,7 +222,7 @@ public async Task GivenOneDifferentStudyInstanceUID_WhenStoringWithProvidedStudy
                 DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUid: studyInstanceUID1);
                 DicomFile dicomFile2 = Samples.CreateRandomDicomFile(studyInstanceUid: studyInstanceUID2);
 
-                using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(new[] { dicomFile1, dicomFile2 }, studyInstanceUid: studyInstanceUID1);
+                using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(new[] { dicomFile1, dicomFile2 }, studyInstanceUid: studyInstanceUID1);
 
                 Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
 
@@ -250,7 +253,7 @@ public async Task GivenDatasetWithDuplicateIdentifiers_WhenStoring_TheServerShou
             var studyInstanceUID = TestUidGenerator.Generate();
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUID, studyInstanceUID);
 
-            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.StoreAsync(new[] { dicomFile1 }));
+            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _instancesManager.StoreAsync(new[] { dicomFile1 }));
 
             Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
 
@@ -272,7 +275,7 @@ public async Task GivenExistingDataset_WhenStoring_TheServerShouldReturnConflict
             {
                 DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUID);
 
-                using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(new[] { dicomFile1 });
+                using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(new[] { dicomFile1 });
 
                 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
 
@@ -285,7 +288,7 @@ public async Task GivenExistingDataset_WhenStoring_TheServerShouldReturnConflict
                 Assert.False(dataset.TryGetSequence(DicomTag.FailedSOPSequence, out DicomSequence _));
 
                 DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(
-                    () => _client.StoreAsync(new[] { dicomFile1 }));
+                    () => _instancesManager.StoreAsync(new[] { dicomFile1 }));
 
                 Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
 
@@ -306,7 +309,7 @@ public async Task GivenDatasetWithInvalidVrValue_WhenStoring_TheServerShouldRetu
 
             DicomFile dicomFile1 = Samples.CreateRandomDicomFileWithInvalidVr(studyInstanceUID);
 
-            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.StoreAsync(new[] { dicomFile1 }));
+            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _instancesManager.StoreAsync(new[] { dicomFile1 }));
 
             Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
             Assert.False(exception.ResponseDataset.TryGetSequence(DicomTag.ReferencedSOPSequence, out DicomSequence _));
@@ -323,7 +326,7 @@ public async Task GivenDatasetWithInvalidUid_WhenStoring_TheServerShouldReturnCo
         {
             DicomFile dicomFile1 = Samples.CreateRandomDicomFile(studyInstanceUID, validateItems: false);
 
-            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _client.StoreAsync(new[] { dicomFile1 }));
+            DicomWebException exception = await Assert.ThrowsAsync<DicomWebException>(() => _instancesManager.StoreAsync(new[] { dicomFile1 }));
 
             Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode);
 
@@ -343,7 +346,7 @@ public async Task StoreSinglepart_ServerShouldReturnOK()
             await using MemoryStream stream = _recyclableMemoryStreamManager.GetStream();
             await dicomFile.SaveAsync(stream);
 
-            using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(stream);
+            using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(stream, instanceId: DicomInstanceId.FromDicomFile(dicomFile));
             Assert.Equal(HttpStatusCode.OK, response.StatusCode);
         }
 
@@ -353,7 +356,7 @@ public async Task StoreSinglepartWithStudyUID_ServerShouldReturnOK()
             var studyInstanceUID = TestUidGenerator.Generate();
             DicomFile dicomFile = Samples.CreateRandomDicomFile(studyInstanceUid: studyInstanceUID);
 
-            using DicomWebResponse<DicomDataset> response = await _client.StoreAsync(dicomFile, studyInstanceUid: studyInstanceUID);
+            using DicomWebResponse<DicomDataset> response = await _instancesManager.StoreAsync(dicomFile, studyInstanceUid: studyInstanceUID);
             Assert.Equal(HttpStatusCode.OK, response.StatusCode);
         }
 
@@ -365,6 +368,12 @@ public static IEnumerable<object[]> GetIncorrectAcceptHeaders
                 yield return new object[] { "application/data" };
             }
         }
+        public Task InitializeAsync() => Task.CompletedTask;
+
+        public async Task DisposeAsync()
+        {
+            await _instancesManager.DisposeAsync();
+        }
 
         private (string SopInstanceUid, string RetrieveUri, string SopClassUid) ConvertToReferencedSopSequenceEntry(DicomDataset dicomDataset)
         {