From 3f90f3274aeec2d1cca08deb3080b1ede5651d75 Mon Sep 17 00:00:00 2001 From: Giuseppe Latrofa Date: Thu, 9 Jul 2026 15:19:42 +0200 Subject: [PATCH 1/4] tests: file encodings --- .../CodeFixes/FixCaepActr702.cs | 18 +++++++++++++++--- .../Fixtures/ActorCodeFixVerifier.cs | 13 ++++++++++--- .../Command/SqlTextBuilderTests+Fixture.cs | 15 ++++++++++----- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/Actors.Analyzer/CodeFixes/FixCaepActr702.cs b/src/Actors.Analyzer/CodeFixes/FixCaepActr702.cs index 1fa99db..e868476 100644 --- a/src/Actors.Analyzer/CodeFixes/FixCaepActr702.cs +++ b/src/Actors.Analyzer/CodeFixes/FixCaepActr702.cs @@ -1,4 +1,4 @@ -using CodeArchitects.Platform.Actors.Analyzer.Utils; +using CodeArchitects.Platform.Actors.Analyzer.Utils; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp; @@ -63,6 +63,7 @@ private static Document ImplementActorMessageInterface(MessageTypeInfo info, INa bool isIdTypeAccessible = idTypeSymbol.IsAccessible(semanticModel, @class.SpanStart); GenericNameSyntax newBaseTypeName = CreateNewBaseTypeName(idTypeSymbol); + SyntaxTrivia endOfLine = GetEndOfLine(root); root = baseType is null ? AddActorMessageInterface(root, info.Class, newBaseTypeName) @@ -87,7 +88,7 @@ private static Document ImplementActorMessageInterface(MessageTypeInfo info, INa semicolonToken: SyntaxFactory.Token( leading: default, kind: SyntaxKind.SemicolonToken, - trailing: SyntaxFactory.TriviaList(SyntaxFactory.LineFeed)))); + trailing: SyntaxFactory.TriviaList(endOfLine)))); } if (!isIdTypeAccessible) @@ -119,12 +120,23 @@ private static Document ImplementActorMessageInterface(MessageTypeInfo info, INa semicolonToken: SyntaxFactory.Token( leading: default, kind: SyntaxKind.SemicolonToken, - trailing: SyntaxFactory.TriviaList(SyntaxFactory.LineFeed)))); + trailing: SyntaxFactory.TriviaList(endOfLine)))); } return document.WithSyntaxRoot(root); } + private static SyntaxTrivia GetEndOfLine(CompilationUnitSyntax root) + { + foreach (SyntaxTrivia trivia in root.DescendantTrivia(descendIntoTrivia: true)) + { + if (trivia.IsKind(SyntaxKind.EndOfLineTrivia)) + return trivia; + } + + return SyntaxFactory.CarriageReturnLineFeed; + } + private static CompilationUnitSyntax ChangeActorMessageInterface(CompilationUnitSyntax root, BaseTypeSyntax baseType, GenericNameSyntax newBaseTypeName) { return root.ReplaceNode( diff --git a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs index 46999da..d1f8ae1 100644 --- a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs +++ b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; namespace CodeArchitects.Platform.Actors.Analyzer.Fixtures; @@ -48,19 +48,26 @@ public ActorCodeFixVerifier ModifyCompilationOptions(Func diagnostic.WithDefaultPath(path))); } + private static string NormalizeLineEndings(string source) + { + return source.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + } + public static Task Verify(Action specification, CancellationToken cancellationToken = default) { ActorCodeFixTest test = new(); diff --git a/tests/Data.AdoNet.UnitTests/Command/SqlTextBuilderTests+Fixture.cs b/tests/Data.AdoNet.UnitTests/Command/SqlTextBuilderTests+Fixture.cs index 4d5dde2..999888c 100644 --- a/tests/Data.AdoNet.UnitTests/Command/SqlTextBuilderTests+Fixture.cs +++ b/tests/Data.AdoNet.UnitTests/Command/SqlTextBuilderTests+Fixture.cs @@ -1,4 +1,4 @@ -using CodeArchitects.Platform.Data.AdoNet.Fixtures; +using CodeArchitects.Platform.Data.AdoNet.Fixtures; using CodeArchitects.Platform.Data.AdoNet.Fixtures.Models; using CodeArchitects.Platform.Data.AdoNet.Model; using CodeArchitects.Platform.Data.AdoNet.MySQL.Command; @@ -36,10 +36,15 @@ public abstract class SelectTextDataAttribute : DataAttribute return queryCompiler(root, sqlBuilder); }); - yield return new object[] { new SqlTextBuilder(cacheMock.Object, new SQLServerSyntaxProvider()), Root, ExpectedTextSQLServer }; - yield return new object[] { new SqlTextBuilder(cacheMock.Object, new PostgreSQLSyntaxProvider()), Root, ExpectedTextPostgreSQL }; - yield return new object[] { new SqlTextBuilder(cacheMock.Object, new OracleSyntaxProvider()), Root, ExpectedTextOracle }; - yield return new object[] { new SqlTextBuilder(cacheMock.Object, new MySQLSyntaxProvider()), Root, ExpectedTextMySQL }; + yield return new object[] { new SqlTextBuilder(cacheMock.Object, new SQLServerSyntaxProvider()), Root, NormalizeLineEndings(ExpectedTextSQLServer) }; + yield return new object[] { new SqlTextBuilder(cacheMock.Object, new PostgreSQLSyntaxProvider()), Root, NormalizeLineEndings(ExpectedTextPostgreSQL) }; + yield return new object[] { new SqlTextBuilder(cacheMock.Object, new OracleSyntaxProvider()), Root, NormalizeLineEndings(ExpectedTextOracle) }; + yield return new object[] { new SqlTextBuilder(cacheMock.Object, new MySQLSyntaxProvider()), Root, NormalizeLineEndings(ExpectedTextMySQL) }; + } + + private static string NormalizeLineEndings(string text) + { + return text.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); } } From a43eee5ed81af90993843d79f783b635c2483292 Mon Sep 17 00:00:00 2001 From: Giuseppe Latrofa Date: Mon, 13 Jul 2026 19:34:04 +0200 Subject: [PATCH 2/4] tests: Actors.Dapr.AspNetCore.Integration tests start sync between redis and rabbit --- .../TestFixture.cs | 64 ++++++++++++++++++- .../docker-compose.yml | 4 +- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/Actors.Dapr.AspNetCore.IntegrationTests/TestFixture.cs b/tests/Actors.Dapr.AspNetCore.IntegrationTests/TestFixture.cs index ba90714..513ef8b 100644 --- a/tests/Actors.Dapr.AspNetCore.IntegrationTests/TestFixture.cs +++ b/tests/Actors.Dapr.AspNetCore.IntegrationTests/TestFixture.cs @@ -1,5 +1,6 @@ -using CliWrap; +using CliWrap; using CliWrap.Exceptions; +using System.Net; using System.Text; namespace CodeArchitects.Platform.Actors.Dapr.AspNetCore; @@ -7,6 +8,8 @@ namespace CodeArchitects.Platform.Actors.Dapr.AspNetCore; public class TestFixture : IAsyncLifetime { private static readonly string s_dockerComposePath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "../../..")); + private static readonly Uri s_daprHealthUri = new("http://localhost:3500/v1.0/healthz"); + private static readonly Uri s_appHealthUri = new("http://localhost:20100/virtual-actor/binding-disabler"); private readonly IReadOnlyDictionary _envVars; private const string LATEST_FRAMEWORK = "10.0"; @@ -62,7 +65,7 @@ await Cli.Wrap("docker-compose") throw new Exception($"Exception in executing the command. Error log:\n{errorSb}", ex); } - await Task.Delay(5000); + await WaitForServiceReadinessAsync(); } public async Task DisposeAsync() @@ -73,4 +76,61 @@ await Cli.Wrap("docker") .WithEnvironmentVariables(_envVars) .ExecuteAsync(); } + + private async Task WaitForServiceReadinessAsync() + { + using HttpClient http = new(); + using CancellationTokenSource cts = new(TimeSpan.FromSeconds(90)); + + Exception? daprLastError = null; + Exception? appLastError = null; + + while (!cts.Token.IsCancellationRequested) + { + try + { + using HttpResponseMessage response = await http.GetAsync(s_daprHealthUri, cts.Token); + if (response.IsSuccessStatusCode) + { + daprLastError = null; + break; + } + + daprLastError = new InvalidOperationException($"Unexpected Dapr health status code {(int)response.StatusCode}."); + } + catch (Exception ex) + { + daprLastError = ex; + } + + await Task.Delay(1000); + } + + if (daprLastError is not null) + { + throw new TimeoutException($"Dapr sidecar did not become ready at {s_daprHealthUri}.", daprLastError); + } + + while (!cts.Token.IsCancellationRequested) + { + try + { + using HttpResponseMessage response = await http.GetAsync(s_appHealthUri, cts.Token); + if (response.StatusCode == HttpStatusCode.OK) + { + return; + } + + appLastError = new InvalidOperationException($"Unexpected app readiness status code {(int)response.StatusCode}."); + } + catch (Exception ex) + { + appLastError = ex; + } + + await Task.Delay(1000); + } + + throw new TimeoutException($"Actor app did not become ready at {s_appHealthUri}.", appLastError); + } } diff --git a/tests/Actors.Dapr.AspNetCore.IntegrationTests/docker-compose.yml b/tests/Actors.Dapr.AspNetCore.IntegrationTests/docker-compose.yml index 523fa8c..7fc6762 100644 --- a/tests/Actors.Dapr.AspNetCore.IntegrationTests/docker-compose.yml +++ b/tests/Actors.Dapr.AspNetCore.IntegrationTests/docker-compose.yml @@ -10,6 +10,8 @@ services: - HTTP_PORT=${HTTP_PORT} ports: - 20100:${HTTP_PORT} + - 3500:3500 + - 50001:50001 environment: - ASPNETCORE_ENVIRONMENT=Development depends_on: @@ -48,7 +50,7 @@ services: - 5672:5672 - 15672:15672 healthcheck: - test: rabbitmq-diagnostics -q ping + test: ["CMD-SHELL", "rabbitmq-diagnostics -q check_running && rabbitmq-diagnostics -q check_port_connectivity"] interval: 15s timeout: 5s retries: 5 From 4a5e045c38891eb6f867f9fe7f5fa14f68c3caa5 Mon Sep 17 00:00:00 2001 From: Giuseppe Latrofa Date: Mon, 13 Jul 2026 19:37:04 +0200 Subject: [PATCH 3/4] tests: fix Messaging.Dapr.AspNetCore.IntegrationTests sync between dapr and rabbit --- .../TestFixture.cs | 46 ++++++++++++++++++- .../docker-compose.yml | 2 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/Messaging.Dapr.AspNetCore.IntegrationTests/TestFixture.cs b/tests/Messaging.Dapr.AspNetCore.IntegrationTests/TestFixture.cs index 326b242..087ccfe 100644 --- a/tests/Messaging.Dapr.AspNetCore.IntegrationTests/TestFixture.cs +++ b/tests/Messaging.Dapr.AspNetCore.IntegrationTests/TestFixture.cs @@ -1,5 +1,6 @@ using CliWrap; using CliWrap.Exceptions; +using System.Net; using System.Text; namespace CodeArchitects.Platform.Messaging.Dapr.AspNetCore; @@ -7,6 +8,8 @@ namespace CodeArchitects.Platform.Messaging.Dapr.AspNetCore; public class TestFixture : IAsyncLifetime { private static readonly string s_dockerComposePath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "../../..")); + private static readonly Uri s_subscriberWaitBaseUri = new("http://localhost:20001/wait/"); + private static readonly Uri s_publisherSendBaseUri = new("http://localhost:20000/noresult/by-reflection/send/"); private readonly IReadOnlyDictionary _envVars; private const string LATEST_FRAMEWORK = "10.0"; @@ -62,7 +65,7 @@ await Cli.Wrap("docker-compose") throw new Exception($"Exception in executing the command. Error log:\n{errorSb}", ex); } - await Task.Delay(15000); + await WaitForServiceReadinessAsync(); } public async Task DisposeAsync() @@ -73,4 +76,45 @@ await Cli.Wrap("docker") .WithEnvironmentVariables(_envVars) .ExecuteAsync(); } + + private static async Task WaitForServiceReadinessAsync() + { + using HttpClient http = new(); + using CancellationTokenSource cts = new(TimeSpan.FromSeconds(120)); + + Exception? lastError = null; + + while (!cts.Token.IsCancellationRequested) + { + Guid probeId = Guid.NewGuid(); + + try + { + // Subscriber readiness (process up and endpoint mapped) + using HttpResponseMessage waitProbe = await http.GetAsync(new Uri(s_subscriberWaitBaseUri, $"{probeId}?millisecondsTimeout=1"), cts.Token); + if (waitProbe.StatusCode != HttpStatusCode.OK && waitProbe.StatusCode != HttpStatusCode.NoContent) + { + throw new InvalidOperationException($"Unexpected subscriber status code {(int)waitProbe.StatusCode}."); + } + + // Publisher + sidecar + bus readiness (fails with 500 while Dapr/pubsub not ready) + using HttpResponseMessage sendProbe = await http.GetAsync(new Uri(s_publisherSendBaseUri, probeId.ToString()), cts.Token); + if (sendProbe.StatusCode == HttpStatusCode.OK) + { + return; + } + + string body = await sendProbe.Content.ReadAsStringAsync(cts.Token); + throw new InvalidOperationException($"Unexpected publisher status code {(int)sendProbe.StatusCode}. Body: {body}"); + } + catch (Exception ex) + { + lastError = ex; + } + + await Task.Delay(1000); + } + + throw new TimeoutException("Messaging integration services did not become ready in time.", lastError); + } } diff --git a/tests/Messaging.Dapr.AspNetCore.IntegrationTests/docker-compose.yml b/tests/Messaging.Dapr.AspNetCore.IntegrationTests/docker-compose.yml index 683d228..0d27b54 100644 --- a/tests/Messaging.Dapr.AspNetCore.IntegrationTests/docker-compose.yml +++ b/tests/Messaging.Dapr.AspNetCore.IntegrationTests/docker-compose.yml @@ -71,7 +71,7 @@ services: - 5672:5672 - 15672:15672 healthcheck: - test: rabbitmq-diagnostics -q ping + test: ["CMD-SHELL", "rabbitmq-diagnostics -q check_running && rabbitmq-diagnostics -q check_port_connectivity"] interval: 20s timeout: 5s retries: 5 \ No newline at end of file From 5c1aac5b25588c08e5645aaa4026c61b0b446af1 Mon Sep 17 00:00:00 2001 From: Giuseppe Latrofa Date: Tue, 14 Jul 2026 10:03:31 +0200 Subject: [PATCH 4/4] tests: normalized EOF encoding --- .../Fixtures/ActorCodeFixTest.cs | 40 +++++++++++++++++++ .../Fixtures/ActorCodeFixVerifier.cs | 3 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixTest.cs b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixTest.cs index cb15a1c..c0c8d1b 100644 --- a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixTest.cs +++ b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixTest.cs @@ -1,5 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Testing.Verifiers; @@ -12,6 +14,21 @@ internal class ActorCodeFixTest : CSharpCodeFixTest NormalizeSolutionLineEndings(solution)); + + SolutionTransforms.Add((solution, projectId) => + solution.WithOptions(solution.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n"))); } public void ModifyCompilationOptions(Func optionsTransform) @@ -33,4 +50,27 @@ protected override async Task GetProjectCompilationAsync(Project pr return ActorTest.GetProjectCompilation(originalCompilation, cancellationToken); } + + private static Solution NormalizeSolutionLineEndings(Solution solution) + { + foreach (DocumentId documentId in solution.Projects.SelectMany(project => project.DocumentIds)) + { + Document? document = solution.GetDocument(documentId); + if (document is null) + { + continue; + } + + SourceText? sourceText = document.GetTextAsync().GetAwaiter().GetResult(); + string normalized = sourceText.ToString().Replace("\r\n", "\n").Replace("\r", "\n"); + if (normalized == sourceText.ToString()) + { + continue; + } + + solution = solution.WithDocumentText(documentId, SourceText.From(normalized, sourceText.Encoding)); + } + + return solution; + } } \ No newline at end of file diff --git a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs index d1f8ae1..9e70e45 100644 --- a/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs +++ b/tests/Actors.Analyzer.FunctionalTests/Fixtures/ActorCodeFixVerifier.cs @@ -65,7 +65,8 @@ private static void AddSource(SolutionState state, string source, DiagnosticResu private static string NormalizeLineEndings(string source) { - return source.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + // Keep a stable newline convention for verifier snapshots across TFMs and OSes. + return source.Replace("\r\n", "\n").Replace("\r", "\n"); } public static Task Verify(Action specification, CancellationToken cancellationToken = default)