diff --git a/README.md b/README.md
index dbdd477cb5..f2e4c32b88 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ ServiceControl, ServiceControl.Audit, and ServiceControl.Monitoring can be run/d
- Edit the `app.config` file of the instance type that needs to be run/debugged to select which transport and persistence to use.
- The configuration file contains commented settings for each supported transport and persistence. It also provides some guidance on additional required settings for specific persisters.
- ServiceControl works with a RavenDB persistence
- - ServiceControl.Audit can work with RavenDB or an InMemory persistence
+ - ServiceControl.Audit works with a RavenDB persistence
- Run or debug the project as usual
A video demo showing how to set it up is available on the Particular YouTube channel:
diff --git a/src/ProjectReferences.Persisters.Audit.props b/src/ProjectReferences.Persisters.Audit.props
index 0a9c4d0dcb..e729ddae3d 100644
--- a/src/ProjectReferences.Persisters.Audit.props
+++ b/src/ProjectReferences.Persisters.Audit.props
@@ -1,7 +1,6 @@
-
diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs
index 734ec5ba45..2a34743384 100644
--- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs
@@ -1,16 +1,17 @@
-namespace ServiceControl.Audit.AcceptanceTests
+namespace ServiceControl.Audit.AcceptanceTests.RavenDB
{
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Nito.Disposables;
+ using ServiceControl.Audit.AcceptanceTests.TestSupport;
using ServiceControl.Audit.Persistence.RavenDB;
using ServiceControl.Audit.Persistence.Tests;
using ServiceControl.RavenDB;
using TestHelper;
- public class AcceptanceTestStorageConfiguration
+ public class AcceptanceTestStorageConfiguration : IAcceptanceTestStorageConfiguration
{
public string PersistenceType { get; } = "RavenDB";
@@ -18,10 +19,10 @@ public class AcceptanceTestStorageConfiguration
string databaseName;
static readonly SemaphoreSlim databaseLifecycleLock = new SemaphoreSlim(1, 1);
- public async Task> CustomizeSettings()
+ public async Task> CustomizeSettings(CancellationToken cancellationToken = default)
{
databaseName = Guid.NewGuid().ToString("n");
- databaseInstance = await SharedEmbeddedServer.GetInstance();
+ databaseInstance = await SharedEmbeddedServer.GetInstance(cancellationToken);
return new Dictionary
{
@@ -30,21 +31,21 @@ public async Task> CustomizeSettings()
};
}
- public async Task Cleanup()
+ public async Task Cleanup(CancellationToken cancellationToken = default)
{
if (databaseInstance == null)
{
return;
}
- using var _ = await UseDatabaseLifecycleLock();
- await databaseInstance.DeleteDatabase(databaseName);
+ using var _ = await UseDatabaseLifecycleLock(cancellationToken);
+ await databaseInstance.DeleteDatabase(databaseName, cancellationToken);
}
///
/// The shared server cannot perform database lifecycle operations in parallel, take this lock when you
/// need to do one of these operations in a test.
///
- public static async Task UseDatabaseLifecycleLock(CancellationToken cancellationToken = default)
+ public async Task UseDatabaseLifecycleLock(CancellationToken cancellationToken = default)
{
await databaseLifecycleLock.WaitAsync(cancellationToken);
return Disposable.Create(() => databaseLifecycleLock.Release());
diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj
index a22d55be05..ea2e5b6579 100644
--- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj
+++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj
@@ -5,6 +5,10 @@
Raven
+
+
+
+
@@ -24,9 +28,12 @@
-
+
+
+
+
diff --git a/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTest.cs b/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTest.cs
index 133e7e805a..dafe95f451 100644
--- a/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTest.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTest.cs
@@ -72,7 +72,7 @@ public Task Teardown()
protected Action> SetStorageConfiguration = _ => { };
protected Action CustomizeHostBuilder = _ => { };
protected ITransportIntegration TransportIntegration;
- protected AcceptanceTestStorageConfiguration StorageConfiguration;
+ protected IAcceptanceTestStorageConfiguration StorageConfiguration;
ServiceControlComponentBehavior serviceControlRunnerBehavior;
TextWriterTraceListener textWriterTraceListener;
diff --git a/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTestStorageConfiguration.cs b/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTestStorageConfiguration.cs
deleted file mode 100644
index 41f816339d..0000000000
--- a/src/ServiceControl.Audit.AcceptanceTests/AcceptanceTestStorageConfiguration.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace ServiceControl.Audit.AcceptanceTests
-{
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using System.Threading.Tasks;
-
- public class AcceptanceTestStorageConfiguration
- {
- public string PersistenceType { get; } = "InMemory";
-
- public Task> CustomizeSettings() => Task.FromResult>(new Dictionary());
-
- public Task Cleanup() => Task.CompletedTask;
-
- public static Task UseDatabaseLifecycleLock() => Task.FromResult(new EmptyDisposable());
-
- class EmptyDisposable : IDisposable
- {
- public void Dispose() { }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.UnitTests/Infrastructure/MaintenanceModeTests.cs b/src/ServiceControl.Audit.AcceptanceTests/MaintenanceModeTests.cs
similarity index 73%
rename from src/ServiceControl.Audit.UnitTests/Infrastructure/MaintenanceModeTests.cs
rename to src/ServiceControl.Audit.AcceptanceTests/MaintenanceModeTests.cs
index e98b6e1543..85e4ea25fd 100644
--- a/src/ServiceControl.Audit.UnitTests/Infrastructure/MaintenanceModeTests.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests/MaintenanceModeTests.cs
@@ -1,4 +1,4 @@
-namespace ServiceControl.Audit.UnitTests.Infrastructure
+namespace ServiceControl.Audit.AcceptanceTests
{
using System;
using System.Runtime.Loader;
@@ -8,12 +8,12 @@ namespace ServiceControl.Audit.UnitTests.Infrastructure
using Audit.Infrastructure.Settings;
using NUnit.Framework;
- class MaintenanceModeTests
+ class MaintenanceModeTests : AcceptanceTest
{
[Test]
public void Should_refuse_unsupported_persister_before_starting_host()
{
- var settings = new Settings(persisterType: "InMemory")
+ var settings = new Settings(persisterType: StorageConfiguration.PersistenceType)
{
AssemblyLoadContextResolver = static _ => AssemblyLoadContext.Default
};
@@ -21,7 +21,7 @@ public void Should_refuse_unsupported_persister_before_starting_host()
var exception = Assert.ThrowsAsync(async () =>
await new MaintenanceModeCommand().Execute(new HostArguments([]), settings));
- Assert.That(exception.Message, Does.Contain("Maintenance mode is not supported").And.Contain("InMemory"));
+ Assert.That(exception.Message, Does.Contain("Maintenance mode is not supported").And.Contain(StorageConfiguration.PersistenceType));
}
}
}
diff --git a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj
deleted file mode 100644
index a36762973f..0000000000
--- a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- net10.0
- Raven
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/IAcceptanceTestStorageConfiguration.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/IAcceptanceTestStorageConfiguration.cs
new file mode 100644
index 0000000000..ad8c43e24b
--- /dev/null
+++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/IAcceptanceTestStorageConfiguration.cs
@@ -0,0 +1,17 @@
+namespace ServiceControl.Audit.AcceptanceTests.TestSupport;
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+public interface IAcceptanceTestStorageConfiguration
+{
+ string PersistenceType { get; }
+
+ Task> CustomizeSettings(CancellationToken cancellationToken = default);
+
+ Task Cleanup(CancellationToken cancellationToken = default);
+
+ Task UseDatabaseLifecycleLock(CancellationToken cancellationToken = default);
+}
diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentBehavior.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentBehavior.cs
index b95cdd3d62..5165260070 100644
--- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentBehavior.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentBehavior.cs
@@ -13,7 +13,7 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport
class ServiceControlComponentBehavior(
ITransportIntegration transportToUse,
- AcceptanceTestStorageConfiguration persistenceToUse,
+ IAcceptanceTestStorageConfiguration persistenceToUse,
Action setSettings,
Action customConfiguration,
Action> setStorageConfiguration,
diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
index 61ba9459cb..81d73b1386 100644
--- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
@@ -29,7 +29,7 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport
public class ServiceControlComponentRunner(
ITransportIntegration transportToUse,
- AcceptanceTestStorageConfiguration persistenceToUse,
+ IAcceptanceTestStorageConfiguration persistenceToUse,
Action setSettings,
Action customConfiguration,
Action> setStorageConfiguration,
@@ -49,7 +49,7 @@ async Task InitializeServiceControl(ScenarioContext context)
// 1. ConfigurationManager.AppSettings is process-global and not thread-safe
// 2. The embedded RavenDB server does not support concurrent database create/delete/index operations
// The test scenario execution (after this method returns) still runs in parallel.
- using var _ = await AcceptanceTestStorageConfiguration.UseDatabaseLifecycleLock();
+ using var _ = await persistenceToUse.UseDatabaseLifecycleLock();
await InitializeServiceControlCore(context);
}
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/.editorconfig b/src/ServiceControl.Audit.Persistence.InMemory/.editorconfig
deleted file mode 100644
index ff993b49bb..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/.editorconfig
+++ /dev/null
@@ -1,4 +0,0 @@
-[*.cs]
-
-# Justification: ServiceControl app has no synchronization context
-dotnet_diagnostic.CA2007.severity = none
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAttachmentsBodyStorage.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAttachmentsBodyStorage.cs
deleted file mode 100644
index c25dc228cc..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAttachmentsBodyStorage.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using ServiceControl.Audit.Auditing.BodyStorage;
- using ServiceControl.Audit.Persistence;
- using ServiceControl.Infrastructure;
-
- class InMemoryAttachmentsBodyStorage : IBodyStorage
- {
- List messageBodies;
-
- public InMemoryAttachmentsBodyStorage()
- {
- messageBodies = [];
- }
-
- public Task Store(string bodyId, string contentType, int bodySize, Stream bodyStream, CancellationToken cancellationToken = default)
- {
- var messageBody = messageBodies.FirstOrDefault(w => w.BodyId == bodyId);
-
- var needToAdd = false;
- if (messageBody == null)
- {
- messageBody = new MessageBody() { BodyId = bodyId };
- needToAdd = true;
- }
-
- messageBody.BodySize = bodySize;
-
- using (var reader = new BinaryReader(bodyStream))
- {
- messageBody.Content = reader.ReadBytes(bodySize);
- }
-
- messageBody.ContentType = contentType;
- messageBody.Version = DataVersion.FromToken(Guid.NewGuid().ToString("N"));
-
- if (needToAdd)
- {
- messageBodies.Add(messageBody);
- }
-
- return Task.CompletedTask;
- }
-
- public Task TryFetch(string bodyId, CancellationToken cancellationToken = default)
- {
- var messageBody = messageBodies.FirstOrDefault(w => w.BodyId == bodyId);
-
- return Task.FromResult(messageBody == null
- ? MessageBodyView.NotFound()
- : MessageBodyView.FromStream(
- new MemoryStream(messageBody.Content),
- messageBody.ContentType,
- messageBody.BodySize,
- messageBody.Version));
- }
-
- class MessageBody
- {
- public string BodyId { get; set; }
- public string ContentType { get; set; }
- public int BodySize { get; set; }
- public byte[] Content { get; set; }
- public DataVersion Version { get; set; }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditDataStore.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditDataStore.cs
deleted file mode 100644
index 5d34f109fc..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditDataStore.cs
+++ /dev/null
@@ -1,248 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using ServiceControl.Audit.Auditing;
- using ServiceControl.Audit.Auditing.BodyStorage;
- using ServiceControl.Audit.Auditing.MessagesView;
- using ServiceControl.Audit.Infrastructure;
- using ServiceControl.Audit.Persistence;
- using ServiceControl.Infrastructure;
- using ServiceControl.SagaAudit;
-
- class InMemoryAuditDataStore : IAuditMessagesViewDataStore, ISagaHistoryDataStore
- {
- IBodyStorage bodyStorage;
- public List failedAuditImports;
-
- public InMemoryAuditDataStore(IBodyStorage bodyStorage)
- {
- this.bodyStorage = bodyStorage;
- sagaHistories = [];
- messageViews = [];
- processedMessages = [];
- failedAuditImports = [];
- }
-
- public async Task> QuerySagaHistoryById(Guid input, CancellationToken cancellationToken = default)
- {
- var sagaHistory = sagaHistories.FirstOrDefault(w => w.SagaId == input);
-
- if (sagaHistory == null)
- {
- return await Task.FromResult(QueryResult.Empty());
- }
-
- return await Task.FromResult(new QueryResult(sagaHistory, new QueryStatsInfo(DataVersion.None, 1)));
- }
-
- public async Task>> GetMessages(bool includeSystemMessages, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange, CancellationToken cancellationToken = default)
- {
- var matched = messageViews
- .Where(w => (!w.IsSystemMessage || includeSystemMessages) &&
- (timeSentRange == null || !timeSentRange.From.HasValue || w.TimeSent >= timeSentRange.From.Value) &&
- (timeSentRange == null || !timeSentRange.To.HasValue || w.TimeSent <= timeSentRange.To.Value))
- .ToList();
-
- return await Task.FromResult(new QueryResult>(matched, new QueryStatsInfo(DataVersion.None, matched.Count)));
- }
-
- public async Task>> QueryMessages(string keyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange, CancellationToken cancellationToken = default)
- {
- var messages = GetMessageIdsMatchingQuery(keyword);
-
- var matched = messageViews
- .Where(w => messages.Contains(w.MessageId) &&
- (timeSentRange == null || !timeSentRange.From.HasValue || w.TimeSent >= timeSentRange.From.Value) &&
- (timeSentRange == null || !timeSentRange.To.HasValue || w.TimeSent <= timeSentRange.To.Value))
- .ToList();
- return await Task.FromResult(new QueryResult>(matched, new QueryStatsInfo(DataVersion.None, matched.Count())));
- }
-
- public async Task>> QueryMessagesByReceivingEndpointAndKeyword(string endpoint, string keyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange, CancellationToken cancellationToken = default)
- {
- var messages = GetMessageIdsMatchingQuery(keyword);
-
- var matched = messageViews.Where(w => w.ReceivingEndpoint.Name == endpoint && messages.Contains(w.MessageId) &&
- (timeSentRange == null || !timeSentRange.From.HasValue || w.TimeSent >= timeSentRange.From.Value) &&
- (timeSentRange == null || !timeSentRange.To.HasValue || w.TimeSent <= timeSentRange.To.Value))
- .ToList();
- return await Task.FromResult(new QueryResult>(matched, new QueryStatsInfo(DataVersion.None, matched.Count)));
- }
-
- public async Task>> QueryMessagesByReceivingEndpoint(bool includeSystemMessages, string endpointName, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange, CancellationToken cancellationToken = default)
- {
- var matched = messageViews.Where(w => w.ReceivingEndpoint.Name == endpointName &&
- (timeSentRange == null || !timeSentRange.From.HasValue || w.TimeSent >= timeSentRange.From.Value) &&
- (timeSentRange == null || !timeSentRange.To.HasValue || w.TimeSent <= timeSentRange.To.Value))
- .ToList();
- return await Task.FromResult(new QueryResult>(matched, new QueryStatsInfo(DataVersion.None, matched.Count)));
- }
-
- public async Task>> QueryMessagesByConversationId(string conversationId, PagingInfo pagingInfo, SortInfo sortInfo, CancellationToken cancellationToken = default)
- {
- var matched = messageViews.Where(w => w.ConversationId == conversationId).ToList();
- return await Task.FromResult(new QueryResult>(matched, new QueryStatsInfo(DataVersion.None, matched.Count)));
- }
-
- public async Task GetMessageBody(string messageId, CancellationToken cancellationToken = default)
- {
- var result = await GetMessageBodyFromMetadata(messageId, cancellationToken);
-
- if (!result.Found)
- {
- var fromAttachments = await GetMessageBodyFromAttachments(messageId, cancellationToken);
- if (fromAttachments.Found)
- {
- return fromAttachments;
- }
- }
-
- return result;
- }
-
- IList GetMessageIdsMatchingQuery(string keyword)
- {
- return processedMessages
- .Where(pm =>
- {
- if ((pm.MessageMetadata["MessageId"] as string) == keyword)
- {
- return true;
- }
-
- if (TryGet(pm.MessageMetadata, "MessageType") is string messageType && messageType.Contains(keyword))
- {
- return true;
- }
-
- if (pm.Headers.Values.Contains(keyword))
- {
- return true;
- }
-
- return pm.MessageMetadata.ContainsKey("Body") && (pm.MessageMetadata["Body"] as string).Contains(keyword);
- })
- .Select(pm => pm.MessageMetadata["MessageId"] as string)
- .ToList();
- }
-
- async Task GetMessageBodyFromAttachments(string messageId, CancellationToken cancellationToken)
- {
- return await bodyStorage.TryFetch(messageId, cancellationToken);
- }
-
- Task GetMessageBodyFromMetadata(string messageId, CancellationToken cancellationToken)
- {
- var message = processedMessages.FirstOrDefault(pm => (pm.MessageMetadata["MessageId"] as string) == messageId);
-
- if (message == null)
- {
- return Task.FromResult(MessageBodyView.NotFound());
- }
-
- var body = !string.IsNullOrEmpty(message.Body) ? message.Body : TryGet(message.MessageMetadata, "Body") as string;
- var bodySize = (int?)TryGet(message.MessageMetadata, "ContentLength") ?? 0;
- var contentType = TryGet(message.MessageMetadata, "ContentType") as string;
- var bodyNotStored = message.MessageMetadata.ContainsKey("BodyNotStored") && (bool)message.MessageMetadata["BodyNotStored"];
-
- if (bodyNotStored && body == null)
- {
- return Task.FromResult(MessageBodyView.NoContent());
- }
-
- if (body == null)
- {
- return Task.FromResult(MessageBodyView.NotFound());
- }
-
- return Task.FromResult(MessageBodyView.FromString(body, contentType, bodySize, DataVersion.FromToken(messageId)));
- }
-
- public Task>> QueryAuditCounts(string endpointName, CancellationToken cancellationToken = default)
- {
- var hasSent = messageViews.Any(m => m.SendingEndpoint?.Name == endpointName);
- var results = messageViews
- .Where(m => m.ReceivingEndpoint.Name == endpointName && !m.IsSystemMessage)
- .GroupBy(m => m.ProcessedAt.ToUniversalTime().Date)
- .Select(g => new AuditCount
- {
- UtcDate = g.Key,
- Count = g.LongCount()
- })
- .OrderBy(r => r.UtcDate)
- .ToList();
-
- return results.Count == 0 && hasSent
- ? Task.FromResult(new QueryResult>([new AuditCount
- {
- UtcDate = messageViews.First().ProcessedAt.ToUniversalTime().Date,
- Count = 0
- }], QueryStatsInfo.Zero))
- : Task.FromResult(new QueryResult>(results, QueryStatsInfo.Zero));
- }
-
- public Task SaveProcessedMessage(ProcessedMessage processedMessage, CancellationToken cancellationToken = default)
- {
- processedMessage.Id ??= processedMessage.GetProcessingId();
- if (processedMessages.Any(pm => pm.Id == processedMessage.Id))
- {
- return Task.CompletedTask;
- }
-
- processedMessages.Add(processedMessage);
- messageViews.Add(MessagesViewFactory.Create(processedMessage));
-
- return Task.CompletedTask;
- }
-
- public Task SaveSagaSnapshot(SagaSnapshot sagaSnapshot, CancellationToken cancellationToken = default)
- {
- var sagaHistory = sagaHistories.SingleOrDefault(sh => sh.SagaId == sagaSnapshot.SagaId);
-
- if (sagaHistory == null)
- {
- sagaHistory = new SagaHistory
- {
- Id = sagaSnapshot.SagaId,
- SagaId = sagaSnapshot.SagaId,
- SagaType = sagaSnapshot.SagaType,
- };
-
- sagaHistories.Add(sagaHistory);
- }
-
- sagaHistory.Changes.Add(new SagaStateChange
- {
- StartTime = sagaSnapshot.StartTime,
- FinishTime = sagaSnapshot.FinishTime,
- Status = sagaSnapshot.Status,
- StateAfterChange = sagaSnapshot.StateAfterChange,
- InitiatingMessage = sagaSnapshot.InitiatingMessage,
- OutgoingMessages = sagaSnapshot.OutgoingMessages,
- Endpoint = sagaSnapshot.Endpoint
- });
-
- return Task.CompletedTask;
- }
-
- object TryGet(Dictionary metadata, string key)
- {
- if (metadata.TryGetValue(key, out var value))
- {
- return value;
- }
-
- return null;
- }
-
- public Task Setup(CancellationToken cancellationToken = default) => Task.CompletedTask;
-
- List messageViews;
- List processedMessages;
- List sagaHistories;
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWork.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWork.cs
deleted file mode 100644
index 649b916220..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWork.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- using Auditing.BodyStorage;
- using ServiceControl.Audit.Auditing;
- using ServiceControl.Audit.Persistence.UnitOfWork;
- using ServiceControl.SagaAudit;
-
- class InMemoryAuditIngestionUnitOfWork(
- InMemoryAuditDataStore dataStore,
- BodyStorageEnricher bodyStorageEnricher)
- : IAuditIngestionUnitOfWork
- {
- public ValueTask DisposeAsync() => ValueTask.CompletedTask;
-
- // InMemory writes are immediate, so Complete has nothing to commit.
- public Task Complete(CancellationToken cancellationToken = default) => Task.CompletedTask;
-
- public async Task RecordProcessedMessage(ProcessedMessage processedMessage, ReadOnlyMemory body, CancellationToken cancellationToken = default)
- {
- if (!body.IsEmpty)
- {
- await bodyStorageEnricher.StoreAuditMessageBody(body, processedMessage, cancellationToken);
- }
- await dataStore.SaveProcessedMessage(processedMessage, cancellationToken);
- }
-
- public Task RecordSagaSnapshot(SagaSnapshot sagaSnapshot, CancellationToken cancellationToken = default) => dataStore.SaveSagaSnapshot(sagaSnapshot, cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWorkFactory.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWorkFactory.cs
deleted file mode 100644
index d0f0ddf664..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryAuditIngestionUnitOfWorkFactory.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System.Threading;
- using System.Threading.Tasks;
- using ServiceControl.Audit.Auditing.BodyStorage;
- using ServiceControl.Audit.Persistence.UnitOfWork;
-
- class InMemoryAuditIngestionUnitOfWorkFactory(InMemoryAuditDataStore dataStore, BodyStorageEnricher bodyStorageEnricher) : IAuditIngestionUnitOfWorkFactory
- {
- public ValueTask StartNew(int batchSize, CancellationToken cancellationToken = default)
- {
- //The batchSize argument is ignored: the in-memory storage implementation doesn't support batching.
- return new ValueTask(new InMemoryAuditIngestionUnitOfWork(dataStore, bodyStorageEnricher));
- }
-
- public bool CanIngestMore() => true;
-
- public bool SupportsConcurrentBatches => false;
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryFailedAuditStorage.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryFailedAuditStorage.cs
deleted file mode 100644
index e6dbf35e02..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryFailedAuditStorage.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- using ServiceControl.Audit.Auditing;
-
- class InMemoryFailedAuditStorage(InMemoryAuditDataStore dataStore) : IFailedAuditStorage
- {
- public async Task ProcessFailedMessages(Func, CancellationToken, Task> onMessage, CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- foreach (var failedMessage in dataStore.failedAuditImports)
- {
- cancellationToken.ThrowIfCancellationRequested();
- FailedTransportMessage transportMessage = failedMessage.Message;
-
- await onMessage(transportMessage, _ => Task.CompletedTask, cancellationToken);
- }
-
- dataStore.failedAuditImports.Clear();
- }
-
- public Task SaveFailedAuditImport(FailedAuditImport message, CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
- var existing = message.Id == null ? -1 : dataStore.failedAuditImports.FindIndex(x => x.Id == message.Id);
- if (existing >= 0)
- {
- dataStore.failedAuditImports[existing] = message;
- }
- else
- {
- dataStore.failedAuditImports.Add(message);
- }
- return Task.CompletedTask;
- }
-
- public Task GetFailedAuditsCount(CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
- return Task.FromResult(dataStore.failedAuditImports.Count);
- }
- }
-}
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistence.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistence.cs
deleted file mode 100644
index b0a487ef3e..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistence.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using Auditing.BodyStorage;
- using Microsoft.Extensions.DependencyInjection;
- using UnitOfWork;
-
- public class InMemoryPersistence(PersistenceSettings persistenceSettings) : IPersistence
- {
- public void AddPersistence(IServiceCollection services)
- {
- services.AddSingleton(persistenceSettings);
- services.AddSingleton();
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- }
-
- public void AddInstaller(IServiceCollection services)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistenceConfiguration.cs b/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistenceConfiguration.cs
deleted file mode 100644
index 016adef5ec..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/InMemoryPersistenceConfiguration.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System.Collections.Generic;
-
- public class InMemoryPersistenceConfiguration : IPersistenceConfiguration
- {
- public bool SupportsMaintenanceMode => false;
-
- public string Name => "InMemory";
-
- public IEnumerable ConfigurationKeys => new string[0];
-
- public IPersistence Create(PersistenceSettings settings) => new InMemoryPersistence(settings);
- }
-}
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/MessagesViewFactory.cs b/src/ServiceControl.Audit.Persistence.InMemory/MessagesViewFactory.cs
deleted file mode 100644
index 66a66249a2..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/MessagesViewFactory.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-namespace ServiceControl.Audit.Persistence.InMemory
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using Auditing;
- using Auditing.MessagesView;
- using ServiceControl.Audit.Monitoring;
-
- static class MessagesViewFactory
- {
- static ILookup writeableMessageViewProperties
- = typeof(MessagesView).GetProperties(BindingFlags.Instance | BindingFlags.Public)
- .Where(p => p.CanWrite)
- .ToLookup(p => p.Name, StringComparer.InvariantCultureIgnoreCase);
-
- static ILookup propertyMapping
- = new Dictionary
- {
- // Metadata key, Property name
- { "ContentLength", "BodySize" }
- }.ToLookup(
- x => x.Key,
- x => x.Value,
- StringComparer.InvariantCultureIgnoreCase);
-
- public static MessagesView Create(ProcessedMessage message)
- {
- var result = new MessagesView
- {
- Id = message.UniqueMessageId,
- ProcessedAt = message.ProcessedAt,
- Status = MessageStatus.Successful,
- Headers = message.Headers.Select(header => new KeyValuePair(header.Key, header.Value))
- };
-
- foreach (var metadata in message.MessageMetadata)
- {
- foreach (var property in writeableMessageViewProperties[metadata.Key])
- {
- property.SetValue(result, metadata.Value);
- }
- foreach (var mappedName in propertyMapping[metadata.Key])
- {
- foreach (var property in writeableMessageViewProperties[mappedName])
- {
- property.SetValue(result, metadata.Value);
- }
- }
- }
-
- if (message.MessageMetadata.TryGetValue("IsRetried", out var isRetried) && (bool)isRetried)
- {
- result.Status = MessageStatus.ResolvedSuccessfully;
- }
-
- return result;
- }
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/ServiceControl.Audit.Persistence.InMemory.csproj b/src/ServiceControl.Audit.Persistence.InMemory/ServiceControl.Audit.Persistence.InMemory.csproj
deleted file mode 100644
index 8184641fa3..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/ServiceControl.Audit.Persistence.InMemory.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- net10.0
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.InMemory/persistence.manifest b/src/ServiceControl.Audit.Persistence.InMemory/persistence.manifest
deleted file mode 100644
index 2dce91234b..0000000000
--- a/src/ServiceControl.Audit.Persistence.InMemory/persistence.manifest
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "Name": "InMemory",
- "DisplayName": "In-memory",
- "Description": "InMemory ServiceControl Audit persister",
- "AssemblyName": "ServiceControl.Audit.Persistence.InMemory",
- "TypeName": "ServiceControl.Audit.Persistence.InMemory.InMemoryPersistenceConfiguration, ServiceControl.Audit.Persistence.InMemory"
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/PersistenceTestsConfiguration.cs b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/PersistenceTestsConfiguration.cs
index 4d699d2bd9..cc4a3808ed 100644
--- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/PersistenceTestsConfiguration.cs
+++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/PersistenceTestsConfiguration.cs
@@ -14,7 +14,7 @@
using ServiceControl.Audit.Persistence.RavenDB;
using UnitOfWork;
- class PersistenceTestsConfiguration
+ class PersistenceTestsConfiguration : IPersistenceTestsConfiguration
{
public IAuditMessagesViewDataStore MessagesViewStore { get; private set; }
diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj
index 31ca62448b..60be6df82f 100644
--- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj
+++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj
@@ -26,8 +26,7 @@
-
-
+
diff --git a/src/ServiceControl.Audit.Persistence.Tests/IPersistenceTestsConfiguration.cs b/src/ServiceControl.Audit.Persistence.Tests/IPersistenceTestsConfiguration.cs
new file mode 100644
index 0000000000..9ad8eff88f
--- /dev/null
+++ b/src/ServiceControl.Audit.Persistence.Tests/IPersistenceTestsConfiguration.cs
@@ -0,0 +1,29 @@
+namespace ServiceControl.Audit.Persistence.Tests;
+
+using System;
+using System.Threading.Tasks;
+using Auditing.BodyStorage;
+using UnitOfWork;
+
+interface IPersistenceTestsConfiguration
+{
+ string Name { get; }
+
+ IAuditMessagesViewDataStore MessagesViewStore { get; }
+
+ ISagaHistoryDataStore SagaHistoryStore { get; }
+
+ IFailedAuditStorage FailedAuditStorage { get; }
+
+ IBodyStorage BodyStorage { get; }
+
+ IAuditIngestionUnitOfWorkFactory AuditIngestionUnitOfWorkFactory { get; }
+
+ IServiceProvider ServiceProvider { get; }
+
+ Task Configure(Action setSettings);
+
+ Task CompleteDBOperation();
+
+ Task Cleanup();
+}
diff --git a/src/ServiceControl.Audit.Persistence.Tests/InMemory/BodyVersionTests.cs b/src/ServiceControl.Audit.Persistence.Tests/InMemory/BodyVersionTests.cs
deleted file mode 100644
index 34dfef456a..0000000000
--- a/src/ServiceControl.Audit.Persistence.Tests/InMemory/BodyVersionTests.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace ServiceControl.Audit.Persistence.Tests
-{
- using System;
- using System.IO;
- using System.Threading.Tasks;
- using NUnit.Framework;
- using ServiceControl.Audit.Infrastructure;
-
- [TestFixture]
- class BodyVersionTests : PersistenceTestFixture
- {
- [Test]
- public async Task In_memory_list_queries_remain_unversioned()
- {
- var result = await MessagesViewStore.GetMessages(false, new PagingInfo(), new SortInfo("message_id", "asc"));
-
- Assert.That(result.QueryStats.Version.HasValue, Is.False);
- }
-
- [Test]
- public async Task Replacing_a_body_changes_its_validator_without_changing_reads()
- {
- var bodyId = Guid.NewGuid().ToString();
- await BodyStorage.Store(bodyId, "text/plain", 1, new MemoryStream([1]));
- var first = await BodyStorage.TryFetch(bodyId);
- var repeated = await BodyStorage.TryFetch(bodyId);
-
- await BodyStorage.Store(bodyId, "text/plain", 1, new MemoryStream([2]));
- var replaced = await BodyStorage.TryFetch(bodyId);
-
- using (Assert.EnterMultipleScope())
- {
- Assert.That(first.Version.HasValue, Is.True);
- Assert.That(repeated.Version, Is.EqualTo(first.Version));
- Assert.That(replaced.Version, Is.Not.EqualTo(first.Version));
- }
-
- first.StreamContent.Dispose();
- repeated.StreamContent.Dispose();
- replaced.StreamContent.Dispose();
- }
- }
-}
diff --git a/src/ServiceControl.Audit.Persistence.Tests/InMemory/PersistenceTestsConfiguration.cs b/src/ServiceControl.Audit.Persistence.Tests/InMemory/PersistenceTestsConfiguration.cs
deleted file mode 100644
index f1a1a5fde4..0000000000
--- a/src/ServiceControl.Audit.Persistence.Tests/InMemory/PersistenceTestsConfiguration.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-namespace ServiceControl.Audit.Persistence.Tests
-{
- using System;
- using System.Threading.Tasks;
- using Auditing.BodyStorage;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
- using NServiceBus.CustomChecks;
- using ServiceControl.Audit.Persistence.InMemory;
- using UnitOfWork;
-
- class PersistenceTestsConfiguration
- {
- public IAuditMessagesViewDataStore MessagesViewStore { get; private set; }
-
- public ISagaHistoryDataStore SagaHistoryStore { get; private set; }
-
- public IFailedAuditStorage FailedAuditStorage { get; private set; }
-
- public IBodyStorage BodyStorage { get; private set; }
-
- public IAuditIngestionUnitOfWorkFactory AuditIngestionUnitOfWorkFactory { get; private set; }
-
- public IServiceProvider ServiceProvider => host.Services;
-
- public string Name => "InMemory";
-
- public async Task Configure(Action setSettings)
- {
- var config = new InMemoryPersistenceConfiguration();
- var hostBuilder = Host.CreateApplicationBuilder();
- var settings = new PersistenceSettings(TimeSpan.FromHours(1), true, 100000);
-
- setSettings(settings);
-
- var persistence = config.Create(settings);
- persistence.AddPersistence(hostBuilder.Services);
- persistence.AddInstaller(hostBuilder.Services);
-
- var assembly = typeof(InMemoryPersistenceConfiguration).Assembly;
-
- foreach (var type in assembly.DefinedTypes)
- {
- if (type.IsAssignableTo(typeof(ICustomCheck)))
- {
- hostBuilder.Services.AddTransient(typeof(ICustomCheck), type);
- }
- }
-
- host = hostBuilder.Build();
- await host.StartAsync();
-
- MessagesViewStore = host.Services.GetRequiredService();
- SagaHistoryStore = host.Services.GetRequiredService();
- FailedAuditStorage = host.Services.GetRequiredService();
- BodyStorage = host.Services.GetService();
- AuditIngestionUnitOfWorkFactory = host.Services.GetRequiredService();
- }
-
- public Task CompleteDBOperation() => Task.CompletedTask;
-
- public async Task Cleanup()
- {
- await host.StopAsync();
- host.Dispose();
- }
-
- IHost host;
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.Persistence.Tests/PersistenceTestFixture.cs b/src/ServiceControl.Audit.Persistence.Tests/PersistenceTestFixture.cs
index a7cdbd7b32..31e108881d 100644
--- a/src/ServiceControl.Audit.Persistence.Tests/PersistenceTestFixture.cs
+++ b/src/ServiceControl.Audit.Persistence.Tests/PersistenceTestFixture.cs
@@ -17,6 +17,10 @@ abstract class PersistenceTestFixture
public virtual Task Setup()
{
configuration = new PersistenceTestsConfiguration();
+ if (configuration is not IPersistenceTestsConfiguration)
+ {
+ throw new Exception($"{nameof(PersistenceTestsConfiguration)} must implement {nameof(IPersistenceTestsConfiguration)}");
+ }
testCancellationTokenSource = Debugger.IsAttached ? new CancellationTokenSource() : new CancellationTokenSource(TestTimeout);
diff --git a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj
deleted file mode 100644
index 219f89cb81..0000000000
--- a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
- net10.0
- Raven
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ServiceControl.Audit.Persistence/DevelopmentPersistenceLocations.cs b/src/ServiceControl.Audit.Persistence/DevelopmentPersistenceLocations.cs
index ffa49e2575..5c533739b7 100644
--- a/src/ServiceControl.Audit.Persistence/DevelopmentPersistenceLocations.cs
+++ b/src/ServiceControl.Audit.Persistence/DevelopmentPersistenceLocations.cs
@@ -17,7 +17,6 @@ static DevelopmentPersistenceLocations()
if (!string.IsNullOrWhiteSpace(srcFolder) && srcFolder.EndsWith("src"))
{
- ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Audit.Persistence.InMemory"));
ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Audit.Persistence.RavenDB"));
}
}
diff --git a/src/ServiceControl.Audit.Persistence/ServiceControl.Audit.Persistence.csproj b/src/ServiceControl.Audit.Persistence/ServiceControl.Audit.Persistence.csproj
index 9c01837062..8e54463350 100644
--- a/src/ServiceControl.Audit.Persistence/ServiceControl.Audit.Persistence.csproj
+++ b/src/ServiceControl.Audit.Persistence/ServiceControl.Audit.Persistence.csproj
@@ -15,7 +15,7 @@
-
+
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.UnitTests/API/APIApprovals.cs b/src/ServiceControl.Audit.UnitTests/API/APIApprovals.cs
index 78e39fe0eb..94e2302234 100644
--- a/src/ServiceControl.Audit.UnitTests/API/APIApprovals.cs
+++ b/src/ServiceControl.Audit.UnitTests/API/APIApprovals.cs
@@ -172,6 +172,6 @@ public void PlatformSampleSettings()
Approver.Verify(settings);
}
- static Settings CreateTestSettings() => new("LearningTransport", "InMemory");
+ static Settings CreateTestSettings() => new("LearningTransport", "RavenDB");
}
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
index c1066673e7..44ef85404e 100644
--- a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
+++ b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
@@ -50,7 +50,7 @@
"Hostname": "localhost",
"VirtualDirectory": "",
"TransportType": "LearningTransport",
- "PersistenceType": "InMemory",
+ "PersistenceType": "RavenDB",
"AuditQueue": "audit",
"ForwardAuditMessages": false,
"IngestAuditMessages": true,
diff --git a/src/ServiceControl.Audit.Persistence.Tests/PersistenceManifestLibraryTests.cs b/src/ServiceControl.Audit.UnitTests/Infrastructure/PersistenceManifestLibraryTests.cs
similarity index 86%
rename from src/ServiceControl.Audit.Persistence.Tests/PersistenceManifestLibraryTests.cs
rename to src/ServiceControl.Audit.UnitTests/Infrastructure/PersistenceManifestLibraryTests.cs
index f68bc70c67..19b5559ece 100644
--- a/src/ServiceControl.Audit.Persistence.Tests/PersistenceManifestLibraryTests.cs
+++ b/src/ServiceControl.Audit.UnitTests/Infrastructure/PersistenceManifestLibraryTests.cs
@@ -1,4 +1,4 @@
-namespace ServiceControl.Audit.Persistence.Tests
+namespace ServiceControl.Audit.UnitTests.Infrastructure
{
using System.Collections.Generic;
using System.IO;
@@ -95,27 +95,16 @@ public class PersistenceManifestLoadingTests
}
""";
- const string InMemory = """
- {
- "Name": "InMemory",
- "DisplayName": "In-memory",
- "Description": "InMemory ServiceControl Audit persister",
- "AssemblyName": "ServiceControl.Audit.Persistence.InMemory",
- "TypeName": "ServiceControl.Audit.Persistence.InMemory.InMemoryPersistenceConfiguration, ServiceControl.Audit.Persistence.InMemory"
- }
- """;
-
[Test]
public void Legacy_manifest_without_an_assembly_does_not_hide_the_persisters_after_it()
{
var manifests = LoadFrom(new()
{
["RavenDB"] = RavenDB,
- ["RavenDB35"] = RavenDB35,
- ["InMemory"] = InMemory
+ ["RavenDB35"] = RavenDB35
});
- Assert.That(manifests.Select(m => m.Name), Is.EquivalentTo(["RavenDB", "RavenDB35", "InMemory"]));
+ Assert.That(manifests.Select(m => m.Name), Is.EquivalentTo(["RavenDB", "RavenDB35"]));
}
[Test]
@@ -124,10 +113,10 @@ public void Unreadable_manifest_does_not_hide_the_persisters_after_it()
var manifests = LoadFrom(new()
{
["Corrupt"] = "{ this is not json",
- ["InMemory"] = InMemory
+ ["RavenDB"] = RavenDB
});
- Assert.That(manifests.Select(m => m.Name), Is.EqualTo(["InMemory"]));
+ Assert.That(manifests.Select(m => m.Name), Is.EqualTo(["RavenDB"]));
}
static List LoadFrom(Dictionary persisters)
diff --git a/src/ServiceControl.Audit.UnitTests/Infrastructure/When_instance_is_setup.cs b/src/ServiceControl.Audit.UnitTests/Infrastructure/When_instance_is_setup.cs
index 4c2cc1d874..7deb11df5b 100644
--- a/src/ServiceControl.Audit.UnitTests/Infrastructure/When_instance_is_setup.cs
+++ b/src/ServiceControl.Audit.UnitTests/Infrastructure/When_instance_is_setup.cs
@@ -13,6 +13,7 @@
using NServiceBus;
using NServiceBus.Transport;
using NUnit.Framework;
+ using Persistence;
using Transports;
class When_instance_is_setup
@@ -33,9 +34,17 @@ public async Task Should_provision_queues()
TransportManifestLibrary.TransportManifests.Add(manifest);
+ PersistenceManifestLibrary.PersistenceManifests.Add(new PersistenceManifest
+ {
+ Name = "FakePersistence",
+ Location = AppContext.BaseDirectory,
+ AssemblyName = Assembly.GetExecutingAssembly().GetName().Name,
+ TypeName = typeof(FakePersistenceConfiguration).AssemblyQualifiedName
+ });
+
var instanceInputQueueName = "SomeInstanceQueue";
- var settings = new Settings("FakeTransport", "InMemory")
+ var settings = new Settings("FakeTransport", "FakePersistence")
{
InstanceName = instanceInputQueueName,
ForwardAuditMessages = true,
@@ -92,4 +101,24 @@ public Task CreateTransportInfrastructure(string name,
throw new NotImplementedException();
public string ToTransportQualifiedQueueName(string queueName) => queueName;
}
+
+ class FakePersistenceConfiguration : IPersistenceConfiguration
+ {
+ public bool SupportsMaintenanceMode => false;
+
+ public string Name => "FakePersistence";
+
+ public IEnumerable ConfigurationKeys => [];
+
+ public IPersistence Create(PersistenceSettings settings) => new FakePersistence();
+ }
+
+ class FakePersistence : IPersistence
+ {
+ public void AddPersistence(IServiceCollection services) => throw new NotImplementedException();
+
+ public void AddInstaller(IServiceCollection services)
+ {
+ }
+ }
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj
index c6334d7c62..9fcab2fd61 100644
--- a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj
+++ b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj
@@ -7,7 +7,6 @@
-
diff --git a/src/ServiceControl.Audit.UnitTests/Verification/CustomChecksTest.cs b/src/ServiceControl.Audit.UnitTests/Verification/CustomChecksTest.cs
index 18e824d544..030afe14b4 100644
--- a/src/ServiceControl.Audit.UnitTests/Verification/CustomChecksTest.cs
+++ b/src/ServiceControl.Audit.UnitTests/Verification/CustomChecksTest.cs
@@ -19,7 +19,7 @@ class CustomChecksTest
[Test]
public void VerifyCustomChecks()
{
- var settings = (object)new Settings("LearningTransport", "InMemory");
+ var settings = (object)new Settings("LearningTransport", "RavenDB");
var discovered =
from type in typeof(Settings).Assembly.GetTypes()
diff --git a/src/ServiceControl.Audit/App.config b/src/ServiceControl.Audit/App.config
index 6e4cc74869..2e6b60036a 100644
--- a/src/ServiceControl.Audit/App.config
+++ b/src/ServiceControl.Audit/App.config
@@ -19,8 +19,7 @@ These settings are only here so that we can debug ServiceControl while developin
-
-
+
diff --git a/src/ServiceControl.Audit/ServiceControl.Audit.csproj b/src/ServiceControl.Audit/ServiceControl.Audit.csproj
index d8fc99ea76..9df69e711f 100644
--- a/src/ServiceControl.Audit/ServiceControl.Audit.csproj
+++ b/src/ServiceControl.Audit/ServiceControl.Audit.csproj
@@ -57,7 +57,6 @@
-
diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/SagaAudit/When_sending_saga_audit_to_audit_instance.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/SagaAudit/When_sending_saga_audit_to_audit_instance.cs
index 631cb52741..5dbbd1f3e2 100644
--- a/src/ServiceControl.MultiInstance.AcceptanceTests/SagaAudit/When_sending_saga_audit_to_audit_instance.cs
+++ b/src/ServiceControl.MultiInstance.AcceptanceTests/SagaAudit/When_sending_saga_audit_to_audit_instance.cs
@@ -1,7 +1,6 @@
namespace ServiceControl.MultiInstance.AcceptanceTests.SagaAudit
{
using System;
- using System.Configuration;
using System.Linq;
using System.Threading.Tasks;
using AcceptanceTesting;
@@ -15,28 +14,6 @@
class When_sending_saga_audit_to_audit_instance : AcceptanceTest
{
- [SetUp]
- public void ConfigSetup()
- {
- // To configure the SagaAudit plugin
- var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
- var appSettings = (AppSettingsSection)config.GetSection("appSettings");
- appSettings.Settings.Add("ServiceControl/Queue", ServiceControlInstanceName);
- config.Save();
- ConfigurationManager.RefreshSection("appSettings");
- }
-
- [TearDown]
- public void ConfigTeardown()
- {
- // Cleanup the saga audit plugin configuration to not leak into other tests
- var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
- var appSettings = (AppSettingsSection)config.GetSection("appSettings");
- appSettings.Settings.Remove("ServiceControl/Queue");
- config.Save();
- ConfigurationManager.RefreshSection("appSettings");
- }
-
[Test]
public async Task Saga_history_can_be_fetched_from_main_instance()
{
diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj
index cbaba829ae..54a16ec686 100644
--- a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj
+++ b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj
@@ -7,10 +7,10 @@
-
+
-
+
@@ -27,4 +27,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
index c8015c8846..3d0afbbf82 100644
--- a/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
+++ b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
@@ -8,7 +8,6 @@ namespace ServiceControl.MultiInstance.AcceptanceTests.TestSupport
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
- using Audit.AcceptanceTests;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -50,13 +49,14 @@ public ServiceControlComponentRunner(ITransportIntegration transportToUse,
public async Task Initialize(RunDescriptor run)
{
SettingsPerInstance.Clear();
+ storage = new SharedRavenStorageConfiguration();
// The way we are setting up things here means we assume there is only one remote instance. Should we move away from this approach
// parts of the logic in this class would have to be augmented to dynamically spin up multiple audit instances based on some configuration
// currently we don't need that so YAGNI.
auditInstanceComponentRunner = new AuditInstanceTestsSupport.ServiceControlComponentRunner(
transportToUse,
- new AcceptanceTestStorageConfiguration(), auditSettings =>
+ storage, auditSettings =>
{
auditSettings.ServiceControlQueueAddress = PrimaryInstanceSettings.DEFAULT_INSTANCE_NAME;
customServiceControlAuditSettings(auditSettings);
@@ -77,7 +77,7 @@ public async Task Initialize(RunDescriptor run)
primaryInstanceComponentRunner = new PrimaryInstanceTestsSupport.ServiceControlComponentRunner(
transportToUse,
- new ServiceControl.AcceptanceTests.RavenDB.AcceptanceTestStorageConfiguration(), primarySettings =>
+ storage, primarySettings =>
{
primarySettings.RemoteInstances = [auditInstance];
customServiceControlSettings(primarySettings);
@@ -120,6 +120,7 @@ public override async Task Stop(CancellationToken cancellationToken = default)
{
await auditInstanceComponentRunner.Stop(cancellationToken);
await primaryInstanceComponentRunner.Stop(cancellationToken);
+ await storage.Cleanup(cancellationToken);
}
ITransportIntegration transportToUse;
@@ -129,6 +130,7 @@ public override async Task Stop(CancellationToken cancellationToken = default)
Action primaryHostBuilderCustomization;
Action auditHostBuilderCustomization;
Action customServiceControlSettings;
+ SharedRavenStorageConfiguration storage;
Audit.AcceptanceTests.TestSupport.ServiceControlComponentRunner auditInstanceComponentRunner;
PrimaryInstanceTestsSupport.ServiceControlComponentRunner primaryInstanceComponentRunner;
}
diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/SharedRavenStorageConfiguration.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/SharedRavenStorageConfiguration.cs
new file mode 100644
index 0000000000..0b599318fb
--- /dev/null
+++ b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/SharedRavenStorageConfiguration.cs
@@ -0,0 +1,79 @@
+namespace ServiceControl.MultiInstance.AcceptanceTests.TestSupport
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Reactive.Disposables;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using ServiceControl.Persistence.RavenDB;
+ using ServiceControl.Persistence.Tests;
+ using ServiceControl.RavenDB;
+ using TestHelper;
+ using AuditRavenPersistenceConfiguration = ServiceControl.Audit.Persistence.RavenDB.RavenPersistenceConfiguration;
+ using AuditStorageConfiguration = ServiceControl.Audit.AcceptanceTests.TestSupport.IAcceptanceTestStorageConfiguration;
+ using PrimaryInstanceSettings = ServiceBus.Management.Infrastructure.Settings.Settings;
+ using PrimaryStorageConfiguration = ServiceControl.AcceptanceTests.TestSupport.IAcceptanceTestStorageConfiguration;
+
+ // RavenDB runs one embedded server per process, so the primary and the audit instance keep their databases on the same one
+ class SharedRavenStorageConfiguration : PrimaryStorageConfiguration, AuditStorageConfiguration
+ {
+ // Both persisters ship a manifest named RavenDB, and only one of them survives the copy into this project's output folder
+ string PrimaryStorageConfiguration.PersistenceType => TypeNameOf(typeof(RavenPersistenceConfiguration));
+
+ string AuditStorageConfiguration.PersistenceType => TypeNameOf(typeof(AuditRavenPersistenceConfiguration));
+
+ async Task PrimaryStorageConfiguration.CustomizeSettings(PrimaryInstanceSettings settings, CancellationToken cancellationToken)
+ {
+ var databaseName = Guid.NewGuid().ToString("n");
+ databaseInstance = await SharedEmbeddedServer.GetInstance(cancellationToken);
+ databaseNames.Add(databaseName);
+ databaseNames.Add($"{databaseName}-throughput");
+
+ settings.PersisterSpecificSettings = new RavenPersisterSettings
+ {
+ ErrorRetentionPeriod = TimeSpan.FromDays(10),
+ ConnectionString = databaseInstance.ServerUrl,
+ DatabaseName = databaseName,
+ ThroughputDatabaseName = $"{databaseName}-throughput"
+ };
+ }
+
+ async Task> AuditStorageConfiguration.CustomizeSettings(CancellationToken cancellationToken)
+ {
+ var databaseName = Guid.NewGuid().ToString("n");
+ databaseInstance = await SharedEmbeddedServer.GetInstance(cancellationToken);
+ databaseNames.Add(databaseName);
+
+ return new Dictionary
+ {
+ { AuditRavenPersistenceConfiguration.ConnectionStringKey, databaseInstance.ServerUrl },
+ { AuditRavenPersistenceConfiguration.DatabaseNameKey, databaseName }
+ };
+ }
+
+ public async Task Cleanup(CancellationToken cancellationToken = default)
+ {
+ if (databaseInstance == null)
+ {
+ return;
+ }
+ using var _ = await UseDatabaseLifecycleLock(cancellationToken);
+ foreach (var databaseName in databaseNames)
+ {
+ await databaseInstance.DeleteDatabase(databaseName, cancellationToken);
+ }
+ }
+
+ public async Task UseDatabaseLifecycleLock(CancellationToken cancellationToken = default)
+ {
+ await databaseLifecycleLock.WaitAsync(cancellationToken);
+ return Disposable.Create(() => databaseLifecycleLock.Release());
+ }
+
+ static string TypeNameOf(Type type) => $"{type.FullName}, {type.Assembly.GetName().Name}";
+
+ static readonly SemaphoreSlim databaseLifecycleLock = new SemaphoreSlim(1, 1);
+ readonly List databaseNames = [];
+ EmbeddedDatabase databaseInstance;
+ }
+}
diff --git a/src/ServiceControl.Persistence.RavenDB/ServiceControl.Persistence.RavenDB.csproj b/src/ServiceControl.Persistence.RavenDB/ServiceControl.Persistence.RavenDB.csproj
index e121d7e979..64ee49dea8 100644
--- a/src/ServiceControl.Persistence.RavenDB/ServiceControl.Persistence.RavenDB.csproj
+++ b/src/ServiceControl.Persistence.RavenDB/ServiceControl.Persistence.RavenDB.csproj
@@ -46,6 +46,7 @@
+
diff --git a/src/ServiceControl.slnx b/src/ServiceControl.slnx
index 622050f094..5abc0cecec 100644
--- a/src/ServiceControl.slnx
+++ b/src/ServiceControl.slnx
@@ -18,16 +18,13 @@
-
-
-
diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/InstallationTests.Audit_install_should_write_expected_config_file.InMemory.approved.txt b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/InstallationTests.Audit_install_should_write_expected_config_file.InMemory.approved.txt
deleted file mode 100644
index 4e46215a5c..0000000000
--- a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/InstallationTests.Audit_install_should_write_expected_config_file.InMemory.approved.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file