diff --git a/eng/ci/public-build.yml b/eng/ci/public-build.yml index 4d4c1b0bc..48af28311 100644 --- a/eng/ci/public-build.yml +++ b/eng/ci/public-build.yml @@ -31,7 +31,7 @@ resources: ref: refs/tags/release extends: - # The template we extend injects compliance-checks into the pipleine, such as SDL and CodeQL + # The template we extend injects compliance-checks into the pipeline, such as SDL and CodeQL template: v1/1ES.Unofficial.PipelineTemplate.yml@1es parameters: pool: @@ -67,7 +67,7 @@ extends: # Run tests - template: /eng/templates/test.yml@self parameters: - testAssembly: '**\bin\**\netcoreapp3.1\DurableTask.Core.Tests.dll' + testAssembly: '**\bin\**\DurableTask.Core.Tests.dll' - stage: DTFxASValidate dependsOn: [] jobs: @@ -86,7 +86,7 @@ extends: # Run tests - template: /eng/templates/test.yml@self parameters: - testAssembly: '**\bin\**\netcoreapp3.1\DurableTask.AzureStorage.Tests.dll' + testAssembly: '**\bin\**\DurableTask.AzureStorage.Tests.dll' - stage: DTFxEmulatorValidate dependsOn: [] jobs: @@ -105,4 +105,4 @@ extends: # Run tests - template: /eng/templates/test.yml@self parameters: - testAssembly: '**\bin\**\netcoreapp3.1\DurableTask.Emulator.Tests.dll' \ No newline at end of file + testAssembly: '**\bin\**\DurableTask.Emulator.Tests.dll' \ No newline at end of file diff --git a/eng/templates/test.yml b/eng/templates/test.yml index 2cd5615f9..59de51f90 100644 --- a/eng/templates/test.yml +++ b/eng/templates/test.yml @@ -20,16 +20,14 @@ steps: - task: VSTest@2 displayName: 'Run tests' inputs: - testAssemblyVer2: | - ${{ parameters.testAssembly }} - !**\obj\** - testFiltercriteria: 'TestCategory!=DisabledInCI' - vsTestVersion: 17.0 - distributionBatchType: basedOnExecutionTime - platform: 'any cpu' - configuration: 'Debug' - diagnosticsEnabled: True - collectDumpOn: always - rerunFailedTests: true - rerunFailedThreshold: 30 - rerunMaxAttempts: 3 \ No newline at end of file + testAssemblyVer2: ${{ parameters.testAssembly }} + testFiltercriteria: 'TestCategory!=DisabledInCI' + vsTestVersion: 17.0 + distributionBatchType: basedOnExecutionTime + platform: 'any cpu' + configuration: 'Debug' + diagnosticsEnabled: True + collectDumpOn: always + rerunFailedTests: true + rerunFailedThreshold: 30 + rerunMaxAttempts: 3 \ No newline at end of file diff --git a/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj b/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj index 19d08a87e..acb32db3b 100644 --- a/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj +++ b/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj @@ -13,7 +13,8 @@ true embedded false - + .\README.md + NU5125;NU5048;CS7035 @@ -40,7 +41,7 @@ - + @@ -53,6 +54,10 @@ + + + + true diff --git a/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs b/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs index 4b7e1af7c..bcb3f1f2c 100644 --- a/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs +++ b/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs @@ -513,7 +513,7 @@ public override async IAsyncEnumerable GetStateAsync(IEnumer yield break; } - IEnumerable> instanceQueries = instanceIds.Select(instance => this.GetStateAsync(instance, allExecutions: true, fetchInput: false, cancellationToken).SingleAsync().AsTask()); + IEnumerable> instanceQueries = instanceIds.Select(instance => this.GetStateAsync(instance, allExecutions: true, fetchInput: false, cancellationToken).SingleOrDefaultAsync().AsTask()); foreach (OrchestrationState state in await Task.WhenAll(instanceQueries)) { if (state != null) diff --git a/src/DurableTask.Core/DurableTask.Core.csproj b/src/DurableTask.Core/DurableTask.Core.csproj index 786e1e42c..830f0b116 100644 --- a/src/DurableTask.Core/DurableTask.Core.csproj +++ b/src/DurableTask.Core/DurableTask.Core.csproj @@ -25,6 +25,7 @@ $(VersionPrefix).$(FileVersionRevision) $(MajorVersion).$(MinorVersion).0.0 + .\README.md @@ -38,12 +39,16 @@ - + + + + + true diff --git a/src/DurableTask.Core/TaskActivityDispatcher.cs b/src/DurableTask.Core/TaskActivityDispatcher.cs index c9e402401..1c22c307f 100644 --- a/src/DurableTask.Core/TaskActivityDispatcher.cs +++ b/src/DurableTask.Core/TaskActivityDispatcher.cs @@ -23,6 +23,7 @@ namespace DurableTask.Core using DurableTask.Core.Logging; using DurableTask.Core.Middleware; using DurableTask.Core.Tracing; + using ActivityStatusCode = Tracing.ActivityStatusCode; /// /// Dispatcher for task activities to handle processing and renewing of work items diff --git a/src/DurableTask.Core/TaskOrchestrationDispatcher.cs b/src/DurableTask.Core/TaskOrchestrationDispatcher.cs index fc051994f..61864a1a5 100644 --- a/src/DurableTask.Core/TaskOrchestrationDispatcher.cs +++ b/src/DurableTask.Core/TaskOrchestrationDispatcher.cs @@ -28,6 +28,7 @@ namespace DurableTask.Core using DurableTask.Core.Middleware; using DurableTask.Core.Serializing; using DurableTask.Core.Tracing; + using ActivityStatusCode = Tracing.ActivityStatusCode; /// /// Dispatcher for orchestrations to handle processing and renewing, completion of orchestration events diff --git a/src/DurableTask.Core/Tracing/TraceHelper.cs b/src/DurableTask.Core/Tracing/TraceHelper.cs index 143ba8af6..37f09c9d5 100644 --- a/src/DurableTask.Core/Tracing/TraceHelper.cs +++ b/src/DurableTask.Core/Tracing/TraceHelper.cs @@ -85,7 +85,7 @@ public class TraceHelper DateTimeOffset startTime = startEvent.ParentTraceContext.ActivityStartTime ?? default; Activity? activity = ActivityTraceSource.StartActivity( - name: activityName, + activityName, kind: activityKind, parentContext: activityContext, startTime: startTime); @@ -139,7 +139,7 @@ public class TraceHelper } Activity? newActivity = ActivityTraceSource.StartActivity( - name: CreateSpanName(TraceActivityConstants.Activity, scheduledEvent.Name, scheduledEvent.Version), + CreateSpanName(TraceActivityConstants.Activity, scheduledEvent.Name, scheduledEvent.Version), kind: ActivityKind.Server, parentContext: activityContext); @@ -180,7 +180,7 @@ public class TraceHelper } Activity? newActivity = ActivityTraceSource.StartActivity( - name: CreateSpanName(TraceActivityConstants.Activity, taskScheduledEvent.Name, taskScheduledEvent.Version), + CreateSpanName(TraceActivityConstants.Activity, taskScheduledEvent.Name, taskScheduledEvent.Version), kind: ActivityKind.Client, startTime: taskScheduledEvent.Timestamp, parentContext: Activity.Current?.Context ?? default); @@ -274,7 +274,7 @@ internal static void EmitTraceActivityForTaskFailed( } Activity? activity = ActivityTraceSource.StartActivity( - name: CreateSpanName(TraceActivityConstants.Orchestration, createdEvent.Name, createdEvent.Version), + CreateSpanName(TraceActivityConstants.Orchestration, createdEvent.Name, createdEvent.Version), kind: ActivityKind.Client, startTime: createdEvent.Timestamp, parentContext: Activity.Current?.Context ?? default); @@ -358,7 +358,7 @@ internal static void EmitTraceActivityForSubOrchestrationFailed( string? targetInstanceId) { Activity? newActivity = ActivityTraceSource.StartActivity( - name: CreateSpanName(TraceActivityConstants.OrchestrationEvent, eventRaisedEvent.Name, null), + CreateSpanName(TraceActivityConstants.OrchestrationEvent, eventRaisedEvent.Name, null), kind: ActivityKind.Producer, parentContext: Activity.Current?.Context ?? default); @@ -391,7 +391,7 @@ internal static void EmitTraceActivityForSubOrchestrationFailed( internal static Activity? StartActivityForNewEventRaisedFromClient(EventRaisedEvent eventRaised, OrchestrationInstance instance) { Activity? newActivity = ActivityTraceSource.StartActivity( - name: CreateSpanName(TraceActivityConstants.OrchestrationEvent, eventRaised.Name, null), + CreateSpanName(TraceActivityConstants.OrchestrationEvent, eventRaised.Name, null), kind: ActivityKind.Producer, parentContext: Activity.Current?.Context ?? default, tags: new KeyValuePair[] @@ -418,7 +418,7 @@ internal static void EmitTraceActivityForTimer( TimerFiredEvent timerFiredEvent) { Activity? newActivity = ActivityTraceSource.StartActivity( - name: CreateTimerSpanName(orchestrationName), + CreateTimerSpanName(orchestrationName), kind: ActivityKind.Internal, startTime: startTime, parentContext: Activity.Current?.Context ?? default); diff --git a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs index 8e8f7ac69..7dadb01ee 100644 --- a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs +++ b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs @@ -2252,6 +2252,7 @@ await Task.WhenAll( /// End-to-end test which validates a simple orchestrator function that calls an activity function /// and checks the OpenTelemetry trace information /// + [TestCategory("DisabledInCI")] [DataTestMethod] [DataRow(true)] [DataRow(false)] @@ -2344,6 +2345,7 @@ public async Task OpenTelemetry_SayHelloWithActivity(bool enableExtendedSessions /// End-to-end test which validates a simple orchestrator function that waits for an external event /// raised through the RaiseEvent API and checks the OpenTelemetry trace information /// + [TestCategory("DisabledInCI")] [DataTestMethod] [DataRow(true)] [DataRow(false)] @@ -2445,6 +2447,7 @@ public async Task OpenTelemetry_ExternalEvent_RaiseEvent(bool enableExtendedSess /// End-to-end test which validates a simple orchestrator function that waits for an external event /// raised by calling SendEvent and checks the OpenTelemetry trace information /// + [TestCategory("DisabledInCI")] [DataTestMethod] [DataRow(true)] [DataRow(false)] diff --git a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj index 0fd66ca7f..7759315b9 100644 --- a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj +++ b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj @@ -5,28 +5,31 @@ netcoreapp3.1;net462 - - - - + + - - - + + + + + + + @@ -48,9 +51,6 @@ Always - - PreserveNewest - \ No newline at end of file diff --git a/test/DurableTask.AzureStorage.Tests/TestHelpers.cs b/test/DurableTask.AzureStorage.Tests/TestHelpers.cs index 3608d0120..1b8740e2b 100644 --- a/test/DurableTask.AzureStorage.Tests/TestHelpers.cs +++ b/test/DurableTask.AzureStorage.Tests/TestHelpers.cs @@ -31,6 +31,8 @@ public static TestOrchestrationHost GetTestOrchestrationHost( { string storageConnectionString = GetTestStorageAccountConnectionString(); + // TODO: update Microsoft.Extensions.Logging to avoid the following warning suppression +#pragma warning disable CS0618 // Type or member is obsolete var settings = new AzureStorageOrchestrationServiceSettings { ExtendedSessionIdleTimeout = TimeSpan.FromSeconds(extendedSessionTimeoutInSeconds), @@ -40,9 +42,9 @@ public static TestOrchestrationHost GetTestOrchestrationHost( TaskHubName = GetTestTaskHubName(), // Setting up a logger factory to enable the new DurableTask.Core logs - // TODO: Add a logger provider so we can collect these logs in memory. - LoggerFactory = new LoggerFactory(), + LoggerFactory = new LoggerFactory().AddConsole(LogLevel.Trace), }; +#pragma warning restore CS0618 // Type or member is obsolete // Give the caller a chance to make test-specific changes to the settings modifySettingsAction?.Invoke(settings); diff --git a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs index c7d945aab..8afbfc545 100644 --- a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs +++ b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs @@ -446,7 +446,6 @@ await WaitForConditionAsync( // Start with four workers and four partitions. Then kill three workers. // Test that the remaining worker will take all the partitions. - [TestCategory("DisabledInCI")] [TestMethod] public async Task TestKillThreeWorker() { @@ -586,7 +585,6 @@ await WaitForConditionAsync( /// Ensure that all instances should be processed sucessfully. /// /// - [TestCategory("DisabledInCI")] [TestMethod] public async Task EnsureOwnedQueueExclusive() { diff --git a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs index ad89efc92..cee91ef9e 100644 --- a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs +++ b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs @@ -10,6 +10,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ---------------------------------------------------------------------------------- +#if !NET462 // for some reasons these tests are not discoverable on 1ES, leading to the test getting aborted. TODO: Needs investigation #nullable enable namespace DurableTask.Core.Tests { @@ -27,6 +28,8 @@ namespace DurableTask.Core.Tests using DurableTask.Core.History; using DurableTask.Emulator; using DurableTask.Test.Orchestrations; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Logging.Console; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -36,23 +39,34 @@ public class DispatcherMiddlewareTests TaskHubClient client = null!; [TestInitialize] - public async Task Initialize() + public void InitializeTests() { + // configure logging so traces are emitted during tests. + // This facilitates debugging when tests fail. + + // TODO: update Microsoft.Extensions.Logging to avoid the following warning suppression +#pragma warning disable CS0618 // Type or member is obsolete + var loggerFactory = new LoggerFactory().AddConsole(LogLevel.Trace); +#pragma warning restore CS0618 // Type or member is obsolete var service = new LocalOrchestrationService(); - this.worker = new TaskHubWorker(service); + this.worker = new TaskHubWorker(service, loggerFactory); - await this.worker + // We use `GetAwaiter().GetResult()` because otherwise this method will fail with: + // "X has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter." + this.worker .AddTaskOrchestrations(typeof(SimplestGreetingsOrchestration), typeof(ParentWorkflow), typeof(ChildWorkflow)) .AddTaskActivities(typeof(SimplestGetUserTask), typeof(SimplestSendGreetingTask)) - .StartAsync(); + .StartAsync().GetAwaiter().GetResult(); this.client = new TaskHubClient(service); } [TestCleanup] - public async Task TestCleanup() + public void CleanupTests() { - await this.worker!.StopAsync(true); + // We use `GetAwaiter().GetResult()` because otherwise this method will fail with: + // "X has wrong signature. The method must be non-static, public, does not return a value and should not take any parameter." + this.worker!.StopAsync(true).GetAwaiter().GetResult(); } [TestMethod] @@ -440,3 +454,4 @@ public async Task MockActivityOrchestration() } } } +#endif \ No newline at end of file diff --git a/test/DurableTask.Core.Tests/DurableTask.Core.Tests.csproj b/test/DurableTask.Core.Tests/DurableTask.Core.Tests.csproj index 8796c5fe8..799f6d93d 100644 --- a/test/DurableTask.Core.Tests/DurableTask.Core.Tests.csproj +++ b/test/DurableTask.Core.Tests/DurableTask.Core.Tests.csproj @@ -5,19 +5,13 @@ netcoreapp3.1;net462 - - - - - - - - + - - - - + + + + + diff --git a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs index ed41ea931..f22825166 100644 --- a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs +++ b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs @@ -19,6 +19,7 @@ namespace DurableTask.Core.Tests using System.Threading.Tasks; using DurableTask.Core.Exceptions; using DurableTask.Emulator; + using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; @@ -32,9 +33,17 @@ public class ExceptionHandlingIntegrationTests public ExceptionHandlingIntegrationTests() { + // configure logging so traces are emitted during tests. + // This facilitates debugging when tests fail. + + // TODO: update Microsoft.Extensions.Logging to avoid the following warning suppression +#pragma warning disable CS0618 // Type or member is obsolete + var loggerFactory = new LoggerFactory().AddConsole(LogLevel.Trace); +#pragma warning restore CS0618 // Type or member is obsolete + var service = new LocalOrchestrationService(); - this.worker = new TaskHubWorker(service); - this.client = new TaskHubClient(service); + this.worker = new TaskHubWorker(service, loggerFactory); + this.client = new TaskHubClient(service, loggerFactory: loggerFactory); } [DataTestMethod] diff --git a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs index a8a711724..eddd9b12c 100644 --- a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs +++ b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs @@ -31,7 +31,7 @@ public async Task Invoke_WithFailingRetryCall_ShouldThrowCorrectException() await Assert.ThrowsExceptionAsync(Invoke, "Interceptor should throw the original exception after exceeding max retry attempts."); } - [TestMethod] + [DataTestMethod] [DataRow(1)] [DataRow(2)] [DataRow(3)] @@ -59,7 +59,7 @@ public async Task Invoke_WithFailingRetryCall_ShouldHaveCorrectNumberOfCalls(int Assert.AreEqual(maxAttempts, callCount, 0, $"There should be {maxAttempts} function calls for {maxAttempts} max attempts."); } - [TestMethod] + [DataTestMethod] [DataRow(1)] [DataRow(2)] [DataRow(3)] diff --git a/test/DurableTask.Core.Tests/app.config b/test/DurableTask.Core.Tests/app.config index d1d912b64..8120452b7 100644 --- a/test/DurableTask.Core.Tests/app.config +++ b/test/DurableTask.Core.Tests/app.config @@ -6,11 +6,5 @@ - - - - - - \ No newline at end of file diff --git a/test/DurableTask.Emulator.Tests/DurableTask.Emulator.Tests.csproj b/test/DurableTask.Emulator.Tests/DurableTask.Emulator.Tests.csproj index f73cb8173..c3c6edfee 100644 --- a/test/DurableTask.Emulator.Tests/DurableTask.Emulator.Tests.csproj +++ b/test/DurableTask.Emulator.Tests/DurableTask.Emulator.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/test/DurableTask.Test.Orchestrations/SimpleOrchestrations.cs b/test/DurableTask.Test.Orchestrations/SimpleOrchestrations.cs index c8505f928..2cdea2d01 100644 --- a/test/DurableTask.Test.Orchestrations/SimpleOrchestrations.cs +++ b/test/DurableTask.Test.Orchestrations/SimpleOrchestrations.cs @@ -409,6 +409,11 @@ public async override Task RunTask(OrchestrationContext context, bool us responderOrchestration = Task.FromResult("Herkimer is done"); } + // before sending the event, wait a few seconds to ensure the sub-orchestrator exists + // otherwise, we risk a race condition where the event is dicarded because the instances table + // does not yet have the sub-orchestrator instance in it. + await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(10), state: null); + // send the id of this orchestration to the responder var responderInstance = new OrchestrationInstance() { InstanceId = responderId }; context.SendEvent(responderInstance, channelName, context.OrchestrationInstance.InstanceId);