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 @@ + + + +