diff --git a/.editorconfig b/.editorconfig
index bbab7044..c18ce75a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -127,6 +127,9 @@ dotnet_diagnostic.CA1724.severity = none
# CA2007 — consider calling ConfigureAwait (not needed in ASP.NET Core)
dotnet_diagnostic.CA2007.severity = none
+# CA1711 — identifiers should not have incorrect suffix (xUnit collections, EventHandler, Permission)
+dotnet_diagnostic.CA1711.severity = none
+
# CA1716 — identifiers should not match keywords
dotnet_diagnostic.CA1716.severity = none
diff --git a/Directory.Build.props b/Directory.Build.props
index 29edb511..9e579562 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -16,6 +16,8 @@
true
true
+
+ $(MSBuildThisFileDirectory)test.runsettings
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 00000000..f8f378b2
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/framework/SimpleModule.Agents/AgentChatService.cs b/framework/SimpleModule.Agents/AgentChatService.cs
index 188270e1..c37b358c 100644
--- a/framework/SimpleModule.Agents/AgentChatService.cs
+++ b/framework/SimpleModule.Agents/AgentChatService.cs
@@ -56,11 +56,7 @@ public async IAsyncEnumerable ChatStreamAsync(
? new FunctionInvokingChatClient(chatClient)
: chatClient;
await foreach (
- var update in client.GetStreamingResponseAsync(
- messages,
- chatOptions,
- cancellationToken
- )
+ var update in client.GetStreamingResponseAsync(messages, chatOptions, cancellationToken)
)
{
foreach (var content in update.Contents)
diff --git a/framework/SimpleModule.Generator/Emitters/LocalizationExtensionsEmitter.cs b/framework/SimpleModule.Generator/Emitters/LocalizationExtensionsEmitter.cs
index c7e7a85f..14b6e969 100644
--- a/framework/SimpleModule.Generator/Emitters/LocalizationExtensionsEmitter.cs
+++ b/framework/SimpleModule.Generator/Emitters/LocalizationExtensionsEmitter.cs
@@ -35,9 +35,7 @@ public void Emit(SourceProductionContext context, DiscoveryData data)
foreach (var module in data.Modules)
{
- sb.AppendLine(
- $" typeof({module.FullyQualifiedName}).Assembly,"
- );
+ sb.AppendLine($" typeof({module.FullyQualifiedName}).Assembly,");
}
sb.AppendLine(" };");
@@ -45,6 +43,9 @@ public void Emit(SourceProductionContext context, DiscoveryData data)
sb.AppendLine(" }");
sb.AppendLine("}");
- context.AddSource("LocalizationExtensions.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
+ context.AddSource(
+ "LocalizationExtensions.g.cs",
+ SourceText.From(sb.ToString(), Encoding.UTF8)
+ );
}
}
diff --git a/framework/SimpleModule.Rag/KnowledgeIndexingHostedService.cs b/framework/SimpleModule.Rag/KnowledgeIndexingHostedService.cs
index 4fd01176..bb0c4bda 100644
--- a/framework/SimpleModule.Rag/KnowledgeIndexingHostedService.cs
+++ b/framework/SimpleModule.Rag/KnowledgeIndexingHostedService.cs
@@ -36,12 +36,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var sourceScope = serviceProvider.CreateScope();
var scopedStore = sourceScope.ServiceProvider.GetRequiredService();
- await IndexSourceAsync(
- scopedStore,
- source,
- sourceScope.ServiceProvider,
- stoppingToken
- );
+ await IndexSourceAsync(scopedStore, source, sourceScope.ServiceProvider, stoppingToken);
});
await Task.WhenAll(tasks);
}
diff --git a/framework/SimpleModule.Rag/VectorKnowledgeStore.cs b/framework/SimpleModule.Rag/VectorKnowledgeStore.cs
index 943c8796..61b4b940 100644
--- a/framework/SimpleModule.Rag/VectorKnowledgeStore.cs
+++ b/framework/SimpleModule.Rag/VectorKnowledgeStore.cs
@@ -57,7 +57,6 @@ public async Task> SearchAsync(
if (!await collection.CollectionExistsAsync(cancellationToken))
return [];
-
var queryEmbeddings = await embeddingGenerator.GenerateAsync(
[query],
cancellationToken: cancellationToken
diff --git a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminPermissionsTests.cs b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminPermissionsTests.cs
index c889f305..ee7b911c 100644
--- a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminPermissionsTests.cs
+++ b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminPermissionsTests.cs
@@ -10,7 +10,8 @@
namespace Admin.Tests.Integration;
-public class AdminPermissionsTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class AdminPermissionsTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminRolesEndpointTests.cs b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminRolesEndpointTests.cs
index 261aba6b..5809a927 100644
--- a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminRolesEndpointTests.cs
+++ b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminRolesEndpointTests.cs
@@ -9,7 +9,8 @@
namespace Admin.Tests.Integration;
-public class AdminRolesEndpointTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class AdminRolesEndpointTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminUsersEndpointTests.cs b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminUsersEndpointTests.cs
index f49af68f..96b31068 100644
--- a/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminUsersEndpointTests.cs
+++ b/modules/Admin/tests/SimpleModule.Admin.Tests/Integration/AdminUsersEndpointTests.cs
@@ -9,7 +9,8 @@
namespace Admin.Tests.Integration;
-public class AdminUsersEndpointTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class AdminUsersEndpointTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/modules/Admin/tests/SimpleModule.Admin.Tests/IntegrationTestCollection.cs b/modules/Admin/tests/SimpleModule.Admin.Tests/IntegrationTestCollection.cs
new file mode 100644
index 00000000..8c4a61db
--- /dev/null
+++ b/modules/Admin/tests/SimpleModule.Admin.Tests/IntegrationTestCollection.cs
@@ -0,0 +1,6 @@
+using SimpleModule.Tests.Shared.Fixtures;
+using Xunit;
+
+[CollectionDefinition(TestCollections.Integration)]
+public sealed class IntegrationTestCollection
+ : ICollectionFixture;
diff --git a/modules/BackgroundJobs/src/SimpleModule.BackgroundJobs/Services/ProgressFlushService.cs b/modules/BackgroundJobs/src/SimpleModule.BackgroundJobs/Services/ProgressFlushService.cs
index af3bf93a..8d7a4090 100644
--- a/modules/BackgroundJobs/src/SimpleModule.BackgroundJobs/Services/ProgressFlushService.cs
+++ b/modules/BackgroundJobs/src/SimpleModule.BackgroundJobs/Services/ProgressFlushService.cs
@@ -42,7 +42,10 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
if (batch.Count > 0)
{
- await FlushBatchAsync(batch, opts.MaxLogEntries, stoppingToken);
+ // Use CancellationToken.None: entries were already consumed from the
+ // channel, so the flush must complete to avoid data loss if StopAsync
+ // cancels stoppingToken mid-save.
+ await FlushBatchAsync(batch, opts.MaxLogEntries, CancellationToken.None);
}
}
}
diff --git a/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsEndpointTests.cs b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsEndpointTests.cs
index 5303beb1..9b2aad5a 100644
--- a/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsEndpointTests.cs
+++ b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsEndpointTests.cs
@@ -8,7 +8,8 @@
namespace BackgroundJobs.Tests.Integration;
-public class BackgroundJobsEndpointTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class BackgroundJobsEndpointTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsViewEndpointTests.cs b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsViewEndpointTests.cs
index 4031577f..4fb85fd6 100644
--- a/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsViewEndpointTests.cs
+++ b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/Integration/BackgroundJobsViewEndpointTests.cs
@@ -5,7 +5,8 @@
namespace BackgroundJobs.Tests.Integration;
-public class BackgroundJobsViewEndpointTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class BackgroundJobsViewEndpointTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/IntegrationTestCollection.cs b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/IntegrationTestCollection.cs
new file mode 100644
index 00000000..8c4a61db
--- /dev/null
+++ b/modules/BackgroundJobs/tests/SimpleModule.BackgroundJobs.Tests/IntegrationTestCollection.cs
@@ -0,0 +1,6 @@
+using SimpleModule.Tests.Shared.Fixtures;
+using Xunit;
+
+[CollectionDefinition(TestCollections.Integration)]
+public sealed class IntegrationTestCollection
+ : ICollectionFixture;
diff --git a/modules/Directory.Build.targets b/modules/Directory.Build.targets
index 746a4a27..ee16ea74 100644
--- a/modules/Directory.Build.targets
+++ b/modules/Directory.Build.targets
@@ -1,4 +1,5 @@
+
+ 0
+ true
+
+
diff --git a/tests/SimpleModule.Benchmarks/SimpleModule.Benchmarks.csproj b/tests/SimpleModule.Benchmarks/SimpleModule.Benchmarks.csproj
index 3a728fa8..cf3d0e6f 100644
--- a/tests/SimpleModule.Benchmarks/SimpleModule.Benchmarks.csproj
+++ b/tests/SimpleModule.Benchmarks/SimpleModule.Benchmarks.csproj
@@ -3,6 +3,8 @@
net10.0
Exe
false
+
+ false
diff --git a/tests/SimpleModule.Cli.Tests/SimpleModule.Cli.Tests.csproj b/tests/SimpleModule.Cli.Tests/SimpleModule.Cli.Tests.csproj
index 6fc0cc61..c62f3dc2 100644
--- a/tests/SimpleModule.Cli.Tests/SimpleModule.Cli.Tests.csproj
+++ b/tests/SimpleModule.Cli.Tests/SimpleModule.Cli.Tests.csproj
@@ -3,6 +3,8 @@
net10.0
false
Exe
+
+ false
diff --git a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs
index 2a16e0d5..9bb98389 100644
--- a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs
+++ b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs
@@ -7,7 +7,8 @@
namespace SimpleModule.Core.Tests.Inertia;
-public class InertiaResultTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class InertiaResultTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/tests/SimpleModule.Core.Tests/Infrastructure/ModuleOptionsTests.cs b/tests/SimpleModule.Core.Tests/Infrastructure/ModuleOptionsTests.cs
index e47932e0..e2943663 100644
--- a/tests/SimpleModule.Core.Tests/Infrastructure/ModuleOptionsTests.cs
+++ b/tests/SimpleModule.Core.Tests/Infrastructure/ModuleOptionsTests.cs
@@ -18,7 +18,8 @@ namespace SimpleModule.Core.Tests.Infrastructure;
/// resolved with their default values. Also verifies that overrides from the host
/// app's Configure{Module}() calls are applied correctly.
///
-public class ModuleOptionsTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class ModuleOptionsTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/tests/SimpleModule.Core.Tests/Infrastructure/WebApplicationFactoryTests.cs b/tests/SimpleModule.Core.Tests/Infrastructure/WebApplicationFactoryTests.cs
index c9eea162..458beb49 100644
--- a/tests/SimpleModule.Core.Tests/Infrastructure/WebApplicationFactoryTests.cs
+++ b/tests/SimpleModule.Core.Tests/Infrastructure/WebApplicationFactoryTests.cs
@@ -16,7 +16,8 @@ namespace SimpleModule.Core.Tests.Infrastructure;
/// missing DbContext replacements, and service resolution deadlocks that
/// would silently hang every integration test.
///
-public class WebApplicationFactoryTests : IClassFixture
+[Collection(TestCollections.Integration)]
+public class WebApplicationFactoryTests
{
private readonly SimpleModuleWebApplicationFactory _factory;
diff --git a/tests/SimpleModule.Core.Tests/IntegrationTestCollection.cs b/tests/SimpleModule.Core.Tests/IntegrationTestCollection.cs
new file mode 100644
index 00000000..8c4a61db
--- /dev/null
+++ b/tests/SimpleModule.Core.Tests/IntegrationTestCollection.cs
@@ -0,0 +1,6 @@
+using SimpleModule.Tests.Shared.Fixtures;
+using Xunit;
+
+[CollectionDefinition(TestCollections.Integration)]
+public sealed class IntegrationTestCollection
+ : ICollectionFixture;
diff --git a/tests/SimpleModule.Tests.Shared/Fixtures/IntegrationTestCollection.cs b/tests/SimpleModule.Tests.Shared/Fixtures/IntegrationTestCollection.cs
new file mode 100644
index 00000000..2655c1aa
--- /dev/null
+++ b/tests/SimpleModule.Tests.Shared/Fixtures/IntegrationTestCollection.cs
@@ -0,0 +1,12 @@
+using SimpleModule.Tests.Shared.Fixtures;
+using Xunit;
+
+///
+/// Shares a single across all test classes
+/// in the same assembly that are decorated with [Collection(TestCollections.Integration)].
+/// This avoids spinning up a separate WebApplicationFactory per test class.
+/// Copy this definition into each test assembly that uses the collection.
+///
+[CollectionDefinition(TestCollections.Integration)]
+public sealed class IntegrationTestCollection
+ : ICollectionFixture;
diff --git a/tests/SimpleModule.Tests.Shared/Fixtures/SimpleModuleWebApplicationFactory.cs b/tests/SimpleModule.Tests.Shared/Fixtures/SimpleModuleWebApplicationFactory.cs
index 7cbcd34e..7fe277a6 100644
--- a/tests/SimpleModule.Tests.Shared/Fixtures/SimpleModuleWebApplicationFactory.cs
+++ b/tests/SimpleModule.Tests.Shared/Fixtures/SimpleModuleWebApplicationFactory.cs
@@ -173,6 +173,7 @@ private void EnsureModuleDatabasesCreated()
EnsureTablesCreated(sp);
EnsureTablesCreated(sp);
EnsureTablesCreated(sp);
+ EnsureTablesCreated(sp);
EnsureTablesCreated(sp);
}
diff --git a/tests/SimpleModule.Tests.Shared/Fixtures/TestCollections.cs b/tests/SimpleModule.Tests.Shared/Fixtures/TestCollections.cs
new file mode 100644
index 00000000..62ca8e4e
--- /dev/null
+++ b/tests/SimpleModule.Tests.Shared/Fixtures/TestCollections.cs
@@ -0,0 +1,6 @@
+namespace SimpleModule.Tests.Shared.Fixtures;
+
+public static class TestCollections
+{
+ public const string Integration = "Integration";
+}
diff --git a/tests/SimpleModule.Tests.Shared/SimpleModule.Tests.Shared.csproj b/tests/SimpleModule.Tests.Shared/SimpleModule.Tests.Shared.csproj
index 23a317f3..70fcbcc2 100644
--- a/tests/SimpleModule.Tests.Shared/SimpleModule.Tests.Shared.csproj
+++ b/tests/SimpleModule.Tests.Shared/SimpleModule.Tests.Shared.csproj
@@ -13,6 +13,10 @@
+
+
+
+