diff --git a/Directory.Packages.props b/Directory.Packages.props index f088054dc..b2a47ce62 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,113 +9,105 @@ - + - - - + + + + - - + + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + + + - + + - - - + + - - - - - - - + + + + + + + - - - + + - + - + + - - + + - - + - - - - - + + + + + - - - - - - - - - - - - - diff --git a/Test/DurableTask.AzureServiceFabric.Tests/AllowedTypesSerializationBinderTests.cs b/Test/DurableTask.AzureServiceFabric.Tests/AllowedTypesSerializationBinderTests.cs index 8e6f58822..fb0231d0b 100644 --- a/Test/DurableTask.AzureServiceFabric.Tests/AllowedTypesSerializationBinderTests.cs +++ b/Test/DurableTask.AzureServiceFabric.Tests/AllowedTypesSerializationBinderTests.cs @@ -81,14 +81,14 @@ public void BindToType_AllowsNullAssemblyName() [TestMethod] public void BindToType_RejectsArbitraryAssembly() { - Assert.ThrowsException(() => + Assert.ThrowsExactly(() => this.binder.BindToType("Evil.Assembly", "Evil.PwnedDescriptor")); } [TestMethod] public void BindToType_RejectsQualifiedArbitraryAssembly() { - Assert.ThrowsException(() => + Assert.ThrowsExactly(() => this.binder.BindToType("Evil.Assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Evil.PwnedDescriptor")); } @@ -97,7 +97,7 @@ public void BindToType_RejectsSystemDiagnosticsProcess() { // A common gadget type — must be rejected var type = typeof(System.Diagnostics.Process); - Assert.ThrowsException(() => + Assert.ThrowsExactly(() => this.binder.BindToType(type.Assembly.GetName().Name, type.FullName)); } @@ -105,7 +105,7 @@ public void BindToType_RejectsSystemDiagnosticsProcess() public void BindToType_RejectsNonAllowlistedMscorlibType() { // System.Type is from mscorlib but not in the type allowlist - Assert.ThrowsException(() => + Assert.ThrowsExactly(() => this.binder.BindToType("mscorlib", typeof(Type).FullName)); } @@ -113,7 +113,7 @@ public void BindToType_RejectsNonAllowlistedMscorlibType() public void BindToType_RejectsUnresolvableType() { // A type name that cannot be resolved should throw a controlled exception, not NullReferenceException - var ex = Assert.ThrowsException(() => + var ex = Assert.ThrowsExactly(() => this.binder.BindToType("DurableTask.Core", "DurableTask.Core.NonExistentType")); StringAssert.Contains(ex.Message, "NonExistentType"); } @@ -123,7 +123,7 @@ public void BindToType_RejectsNonAllowlistedDurableTaskCoreType() { // TaskOrchestration is a DurableTask.Core type but not in the proxy endpoint allowlist var type = typeof(TaskOrchestration); - Assert.ThrowsException(() => + Assert.ThrowsExactly(() => this.binder.BindToType(type.Assembly.GetName().Name, type.FullName)); } @@ -208,7 +208,7 @@ public void Deserialize_MaliciousPayload_IsRejected() }; // Newtonsoft wraps the binder's InvalidOperationException in a JsonSerializationException - var ex = Assert.ThrowsException(() => + var ex = Assert.ThrowsExactly(() => JsonConvert.DeserializeObject(maliciousJson, settings)); Assert.IsInstanceOfType(ex.InnerException, typeof(InvalidOperationException)); StringAssert.Contains(ex.InnerException.Message, "is not allowed"); diff --git a/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs b/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs index f57918638..399ccd2d8 100644 --- a/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs +++ b/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs @@ -30,7 +30,7 @@ public void MissingRequestFailedException() Assert.IsFalse(exception.LeaseLost); } - [DataTestMethod] + [TestMethod] [DataRow(true, HttpStatusCode.Conflict, nameof(BlobErrorCode.LeaseLost))] [DataRow(false, HttpStatusCode.Conflict, nameof(BlobErrorCode.LeaseNotPresentWithBlobOperation))] [DataRow(false, HttpStatusCode.NotFound, nameof(BlobErrorCode.BlobNotFound))] diff --git a/samples/DistributedTraceSample/ApplicationInsights/README.md b/samples/DistributedTraceSample/ApplicationInsights/README.md index cf358239a..3dd3ea50e 100644 --- a/samples/DistributedTraceSample/ApplicationInsights/README.md +++ b/samples/DistributedTraceSample/ApplicationInsights/README.md @@ -4,7 +4,7 @@ This sample demonstrates direct integration with Azure Application Insights for ## Prerequisites -- .NET 6.0 SDK or later +- .NET 8.0 SDK or later - Azure Storage Emulator (Azurite) or Azure Storage account - Azure Application Insights resource diff --git a/samples/DistributedTraceSample/README.md b/samples/DistributedTraceSample/README.md index f74316198..6d68a5a20 100644 --- a/samples/DistributedTraceSample/README.md +++ b/samples/DistributedTraceSample/README.md @@ -44,7 +44,7 @@ services.TryAddEnumerable( ## Prerequisites -- .NET 6.0 SDK or later +- .NET 8.0 SDK or later - Azure Storage Emulator (Azurite) or Azure Storage account - (Optional) Application Insights resource - (Optional) Zipkin instance for OpenTelemetry sample diff --git a/samples/DurableTask.Samples/Options.cs b/samples/DurableTask.Samples/Options.cs index e20bfafa2..1977295b7 100644 --- a/samples/DurableTask.Samples/Options.cs +++ b/samples/DurableTask.Samples/Options.cs @@ -13,16 +13,17 @@ namespace DurableTask.Samples { + using System.Collections.Generic; using CommandLine; using CommandLine.Text; internal class Options { - [Option('c', "create-hub", DefaultValue = false, + [Option('c', "create-hub", Default = false, HelpText = "Create Orchestration Hub.")] public bool CreateHub { get; set; } - [Option('s', "start-instance", DefaultValue = null, + [Option('s', "start-instance", Default = null, HelpText = "Start new instance. Supported Orchestrations: 'Greetings, Cron, Average, ErrorHandling Signal'.")] public string StartInstance { get; set; } @@ -30,20 +31,19 @@ internal class Options HelpText = "Instance id for new orchestration instance.")] public string InstanceId { get; set; } - [OptionArray('p', "params", + [Option('p', "params", HelpText = "Parameters for new instance.")] - public string[] Parameters { get; set; } + public IEnumerable Parameters { get; set; } [Option('n', "signal-name", HelpText = "Instance id to send signal")] public string Signal { get; set; } - [Option('w', "skip-worker", DefaultValue = false, + [Option('w', "skip-worker", Default = false, HelpText = "Don't start worker")] public bool SkipWorker { get; set; } - [HelpOption] - public string GetUsage() + public static string GetUsage(ParserResult options) { // this without using CommandLine.Text // or using HelpText.AutoBuild @@ -63,7 +63,7 @@ public string GetUsage() help.AddPreOptionsLine("Usage: DurableTaskSamples.exe -c -s SumOfSquares"); help.AddPreOptionsLine("Usage: DurableTaskSamples.exe -c -s Signal -i 1"); help.AddPreOptionsLine("Usage: DurableTaskSamples.exe -w -n User -i 1 -p MyName"); - help.AddOptions(this); + help.AddOptions(options); return help; } } diff --git a/samples/DurableTask.Samples/Program.cs b/samples/DurableTask.Samples/Program.cs index 5bef0ed32..9887be7dc 100644 --- a/samples/DurableTask.Samples/Program.cs +++ b/samples/DurableTask.Samples/Program.cs @@ -22,6 +22,7 @@ namespace DurableTask.Samples using System.IO; using System.Linq; using System.Threading; + using CommandLine; using DurableTask.AzureStorage; using DurableTask.Core; using DurableTask.Core.Tracing; @@ -38,7 +39,6 @@ namespace DurableTask.Samples internal class Program { - static readonly Options ArgumentOptions = new Options(); static ObservableEventListener eventListener; [STAThread] @@ -48,8 +48,15 @@ static void Main(string[] args) eventListener.LogToConsole(); eventListener.EnableEvents(DefaultEventSource.Log, EventLevel.LogAlways); - if (CommandLine.Parser.Default.ParseArgumentsStrict(args, ArgumentOptions)) + Options argumentOptions = null; + ParserResult parserResult = Parser.Default.ParseArguments(args); + parserResult + .WithParsed(options => argumentOptions = options) + .WithNotParsed(errors => Console.Error.WriteLine(Options.GetUsage(parserResult))); + + if (argumentOptions != null) { + string[] parameters = argumentOptions.Parameters?.ToArray(); string storageConnectionString = GetSetting("StorageConnectionString"); string taskHubName = ConfigurationManager.AppSettings["taskHubName"]; @@ -63,44 +70,44 @@ static void Main(string[] args) var taskHubClient = new TaskHubClient(orchestrationServiceAndClient); var taskHubWorker = new TaskHubWorker(orchestrationServiceAndClient); - if (ArgumentOptions.CreateHub) + if (argumentOptions.CreateHub) { orchestrationServiceAndClient.CreateIfNotExistsAsync().Wait(); } OrchestrationInstance instance = null; - if (!string.IsNullOrWhiteSpace(ArgumentOptions.StartInstance)) + if (!string.IsNullOrWhiteSpace(argumentOptions.StartInstance)) { - string instanceId = ArgumentOptions.InstanceId ?? Guid.NewGuid().ToString(); - Console.WriteLine($"Start Orchestration: {ArgumentOptions.StartInstance}"); - switch (ArgumentOptions.StartInstance) + string instanceId = argumentOptions.InstanceId ?? Guid.NewGuid().ToString(); + Console.WriteLine($"Start Orchestration: {argumentOptions.StartInstance}"); + switch (argumentOptions.StartInstance) { case "Greetings": instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(GreetingsOrchestration), instanceId, null).Result; break; case "Greetings2": - if (ArgumentOptions.Parameters == null || ArgumentOptions.Parameters.Length != 1) + if (parameters == null || parameters.Length != 1) { throw new ArgumentException("parameters"); } instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(GreetingsOrchestration2), instanceId, - int.Parse(ArgumentOptions.Parameters[0])).Result; + int.Parse(parameters[0])).Result; break; case "Cron": // Sample Input: "0 12 * */2 Mon" instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(CronOrchestration), instanceId, - (ArgumentOptions.Parameters != null && ArgumentOptions.Parameters.Length > 0) ? ArgumentOptions.Parameters[0] : null).Result; + (parameters != null && parameters.Length > 0) ? parameters[0] : null).Result; break; case "Average": // Sample Input: "1 50 10" - if (ArgumentOptions.Parameters == null || ArgumentOptions.Parameters.Length != 3) + if (parameters == null || parameters.Length != 3) { throw new ArgumentException("parameters"); } - int[] input = ArgumentOptions.Parameters.Select(p => int.Parse(p)).ToArray(); + int[] input = parameters.Select(p => int.Parse(p)).ToArray(); instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(AverageCalculatorOrchestration), instanceId, input).Result; break; case "ErrorHandling": @@ -118,46 +125,46 @@ static void Main(string[] args) instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(SignalOrchestration), instanceId, null).Result; break; case "SignalAndRaise": - if (ArgumentOptions.Parameters == null || ArgumentOptions.Parameters.Length != 1) + if (parameters == null || parameters.Length != 1) { throw new ArgumentException("parameters"); } - instance = taskHubClient.CreateOrchestrationInstanceWithRaisedEventAsync(typeof(SignalOrchestration), instanceId, null, ArgumentOptions.Signal, ArgumentOptions.Parameters[0]).Result; + instance = taskHubClient.CreateOrchestrationInstanceWithRaisedEventAsync(typeof(SignalOrchestration), instanceId, null, argumentOptions.Signal, parameters[0]).Result; break; case "Replat": instance = taskHubClient.CreateOrchestrationInstanceAsync(typeof(MigrateOrchestration), instanceId, new MigrateOrchestrationData { SubscriptionId = "03a1cd39-47ac-4a57-9ff5-a2c2a2a76088", IsDisabled = false }).Result; break; default: - throw new Exception("Unsupported Orchestration Name: " + ArgumentOptions.StartInstance); + throw new Exception("Unsupported Orchestration Name: " + argumentOptions.StartInstance); } Console.WriteLine("Workflow Instance Started: " + instance); } - else if (!string.IsNullOrWhiteSpace(ArgumentOptions.Signal)) + else if (!string.IsNullOrWhiteSpace(argumentOptions.Signal)) { Console.WriteLine("Run RaiseEvent"); - if (string.IsNullOrWhiteSpace(ArgumentOptions.InstanceId)) + if (string.IsNullOrWhiteSpace(argumentOptions.InstanceId)) { throw new ArgumentException("instanceId"); } - if (ArgumentOptions.Parameters == null || ArgumentOptions.Parameters.Length != 1) + if (parameters == null || parameters.Length != 1) { throw new ArgumentException("parameters"); } - string instanceId = ArgumentOptions.InstanceId; + string instanceId = argumentOptions.InstanceId; instance = new OrchestrationInstance { InstanceId = instanceId }; - taskHubClient.RaiseEventAsync(instance, ArgumentOptions.Signal, ArgumentOptions.Parameters[0]).Wait(); + taskHubClient.RaiseEventAsync(instance, argumentOptions.Signal, parameters[0]).Wait(); Console.WriteLine("Press any key to quit."); Console.ReadLine(); } - if (!ArgumentOptions.SkipWorker) + if (!argumentOptions.SkipWorker) { try { diff --git a/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs b/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs index abf7a58b2..40f957ef9 100644 --- a/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs +++ b/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs @@ -277,7 +277,9 @@ async Task> DedupeExecutionStartedMessagesAsync( // "Remote" -> the instance ID info comes from the Instances table that we're querying IAsyncEnumerable instances = this.trackingStore.FetchInstanceStatusAsync(instanceIds, cancellationToken); IDictionary remoteOrchestrationsById = - await instances.ToDictionaryAsync(o => o.State.OrchestrationInstance.InstanceId, cancellationToken); + await instances.ToDictionaryAsync( + o => o.State.OrchestrationInstance.InstanceId, + cancellationToken: cancellationToken); foreach (MessageData message in executionStartedMessages) { diff --git a/src/DurableTask.Core/DurableTask.Core.csproj b/src/DurableTask.Core/DurableTask.Core.csproj index f372973a1..c7ffc18ca 100644 --- a/src/DurableTask.Core/DurableTask.Core.csproj +++ b/src/DurableTask.Core/DurableTask.Core.csproj @@ -39,8 +39,7 @@ - - + diff --git a/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj b/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj index 0152c0860..e8d2825b3 100644 --- a/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj +++ b/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj @@ -25,11 +25,16 @@ + + + + + diff --git a/test/DurableTask.AzureServiceFabric.Integration.Tests/App.config b/test/DurableTask.AzureServiceFabric.Integration.Tests/App.config index a62530332..2273cd39f 100644 --- a/test/DurableTask.AzureServiceFabric.Integration.Tests/App.config +++ b/test/DurableTask.AzureServiceFabric.Integration.Tests/App.config @@ -6,22 +6,6 @@ - - - - - - - - - - - - - - - - diff --git a/test/DurableTask.AzureServiceFabric.Integration.Tests/DeploymentUtil/DeploymentHelper.cs b/test/DurableTask.AzureServiceFabric.Integration.Tests/DeploymentUtil/DeploymentHelper.cs index 20cb25ef8..f7f5527f8 100644 --- a/test/DurableTask.AzureServiceFabric.Integration.Tests/DeploymentUtil/DeploymentHelper.cs +++ b/test/DurableTask.AzureServiceFabric.Integration.Tests/DeploymentUtil/DeploymentHelper.cs @@ -54,7 +54,11 @@ public static async Task CleanAsync() var replicas = (await client.QueryManager.GetDeployedReplicaListAsync(node.NodeName, application.ApplicationName)).OfType(); foreach (var replica in replicas) { - await client.ServiceManager.RemoveReplicaAsync(node.NodeName, replica.Partitionid, replica.ReplicaId); + await client.ServiceManager.RemoveReplicaAsync( + node.NodeName, + replica.Partitionid, + replica.ReplicaId, + new RemoveReplicaOptions()); } } } diff --git a/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj b/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj index 09d30327f..1efa6a66a 100644 --- a/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj +++ b/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/test/DurableTask.AzureServiceFabric.Integration.Tests/FunctionalTests.cs b/test/DurableTask.AzureServiceFabric.Integration.Tests/FunctionalTests.cs index 10dc90b28..35f9a33f1 100644 --- a/test/DurableTask.AzureServiceFabric.Integration.Tests/FunctionalTests.cs +++ b/test/DurableTask.AzureServiceFabric.Integration.Tests/FunctionalTests.cs @@ -376,7 +376,6 @@ public async Task QueryState_For_Latest_Execution() } [TestMethod] - [ExpectedException(typeof(OrchestrationAlreadyExistsException))] public async Task Duplicate_Orchestration_Instance_Fails_With_OrchestrationAlreadyExistsException() { var instanceId = nameof(Duplicate_Orchestration_Instance_Fails_With_OrchestrationAlreadyExistsException); @@ -389,8 +388,9 @@ public async Task Duplicate_Orchestration_Instance_Fails_With_OrchestrationAlrea DelayUnit = TimeSpan.FromSeconds(1), }; - var instance = await this.taskHubClient.CreateOrchestrationInstanceAsync(typeof(TestOrchestration), instanceId, input); - var instance2 = await this.taskHubClient.CreateOrchestrationInstanceAsync(typeof(TestOrchestration), instanceId, input); + await this.taskHubClient.CreateOrchestrationInstanceAsync(typeof(TestOrchestration), instanceId, input); + await Assert.ThrowsExactlyAsync( + () => this.taskHubClient.CreateOrchestrationInstanceAsync(typeof(TestOrchestration), instanceId, input)); } [TestMethod] @@ -476,7 +476,7 @@ public async Task ForceTerminate_Already_Finished_Orchestration() var reason = "Testing terminatiom of already finished orchestration"; - await Assert.ThrowsExceptionAsync(() => this.taskHubClient.TerminateInstanceAsync(instance, reason)); + await Assert.ThrowsExactlyAsync(() => this.taskHubClient.TerminateInstanceAsync(instance, reason)); } [TestMethod] @@ -586,7 +586,7 @@ public async Task Purge_Removes_State() public async Task ScheduledStartTest_NotSupported() { var expectedStartTime = DateTime.UtcNow.AddSeconds(30); - await Assert.ThrowsExceptionAsync(() => this.taskHubClient.CreateScheduledOrchestrationInstanceAsync(typeof(SimpleOrchestrationWithTasks), null, expectedStartTime)); + await Assert.ThrowsExactlyAsync(() => this.taskHubClient.CreateScheduledOrchestrationInstanceAsync(typeof(SimpleOrchestrationWithTasks), null, expectedStartTime)); } [TestMethod] @@ -612,7 +612,7 @@ public async Task CreateTaskOrchestration_HandlesConflictResponse_When_HttpClien serviceClient.HttpClient = httpClient; }); - await Assert.ThrowsExceptionAsync(async () => + await Assert.ThrowsExactlyAsync(async () => { await taskHubClient.CreateOrchestrationInstanceAsync(typeof(TestOrchestration), new TestOrchestrationData()); }); diff --git a/test/DurableTask.AzureServiceFabric.Tests/App.config b/test/DurableTask.AzureServiceFabric.Tests/App.config index 38337d7bd..d787588cb 100644 --- a/test/DurableTask.AzureServiceFabric.Tests/App.config +++ b/test/DurableTask.AzureServiceFabric.Tests/App.config @@ -6,22 +6,6 @@ - - - - - - - - - - - - - - - - diff --git a/test/DurableTask.AzureServiceFabric.Tests/DurableTask.AzureServiceFabric.Tests.csproj b/test/DurableTask.AzureServiceFabric.Tests/DurableTask.AzureServiceFabric.Tests.csproj index e409082a0..1e0dc8248 100644 --- a/test/DurableTask.AzureServiceFabric.Tests/DurableTask.AzureServiceFabric.Tests.csproj +++ b/test/DurableTask.AzureServiceFabric.Tests/DurableTask.AzureServiceFabric.Tests.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs b/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs index 29d0b0ef9..4ca66cc55 100644 --- a/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs +++ b/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs @@ -21,14 +21,14 @@ namespace DurableTask.AzureStorage.Tests [TestClass] public class AsyncAutoResetEventTests { - [DataTestMethod] + [TestMethod] [DataRow(false, false)] [DataRow(true, true)] public async Task InitialState(bool initiallySignaled, bool expectedResult) { var resetEvent = new AsyncAutoResetEvent(initiallySignaled); bool result = await resetEvent.WaitAsync(TimeSpan.Zero); - Assert.AreEqual(result, expectedResult); + Assert.AreEqual(expectedResult, result); } [TestMethod] diff --git a/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs b/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs index 7d7ac8a78..660adb9fc 100644 --- a/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs +++ b/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs @@ -210,7 +210,7 @@ private async Task EnsureLeasesMatchControlQueue(string directoryReference, Blob /// REQUIREMENT: Workers can be added or removed at any time and control-queue partitions are load-balanced automatically. /// REQUIREMENT: No two workers will ever process the same control queue. /// - [DataTestMethod] + [TestMethod] [DataRow(PartitionManagerType.V1Legacy, 30)] [DataRow(PartitionManagerType.V2Safe, 180)] public async Task MultiWorkerLeaseMovement(PartitionManagerType partitionManagerType, int timeoutInSeconds) @@ -589,7 +589,7 @@ await TestHelpers.WaitFor( await service2.CompleteTaskOrchestrationWorkItemAsync(workItem2, runtimeState, new List(), new List(), new List(), null, null); // Now worker 1 will attempt to complete the same work item. Since this is the first attempt to complete a work item and add a history for the orchestration (by worker 1), // there is no etag stored for the OrchestrationSession, and so the a "conflict" exception will be thrown as worker 2 already created a history for the orchestration. - SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () => + SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () => await service1.CompleteTaskOrchestrationWorkItemAsync(workItem1, runtimeState, new List(), new List(), new List(), null, null) ); Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException)); @@ -632,7 +632,7 @@ await TestHelpers.WaitFor( await service1.CompleteTaskOrchestrationWorkItemAsync(workItem1, runtimeState, new List(), new List(), new List(), null, null); // Now worker 2 attempts to complete the same work item. Since this is not the first work item for the orchestration, now an etag exists for the OrchestrationSession, and the exception // that is thrown will be "precondition failed" as the Etag is stale after worker 1 completed the work item. - exception = await Assert.ThrowsExceptionAsync(async () => + exception = await Assert.ThrowsExactlyAsync(async () => await service2.CompleteTaskOrchestrationWorkItemAsync(workItem2, runtimeState, new List(), new List(), new List(), null, null) ); Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException)); diff --git a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs index 07b064aca..73533b6ef 100644 --- a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs +++ b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs @@ -50,7 +50,7 @@ public class AzureStorageScenarioTests /// /// End-to-end test which validates a simple orchestrator function which doesn't call any activity functions. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions) @@ -63,8 +63,8 @@ public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(StandardTimeout); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("World", JToken.Parse(status?.Input)); - Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output)); + Assert.AreEqual("World", JToken.Parse(status?.Input).Value()); + Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -73,7 +73,7 @@ public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions) /// /// End-to-end test which runs a simple orchestrator function that calls a single activity function. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task HelloWorldOrchestration_Activity(bool enableExtendedSessions) @@ -86,8 +86,8 @@ public async Task HelloWorldOrchestration_Activity(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(StandardTimeout); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("World", JToken.Parse(status?.Input)); - Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output)); + Assert.AreEqual("World", JToken.Parse(status?.Input).Value()); + Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -107,8 +107,8 @@ public async Task SequentialOrchestration() var status = await client.WaitForCompletionAsync(StandardTimeout); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(10, JToken.Parse(status?.Input)); - Assert.AreEqual(3628800, JToken.Parse(status?.Output)); + Assert.AreEqual(10, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(3628800, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -129,8 +129,8 @@ public async Task SequentialOrchestrationNoReplay() var status = await client.WaitForCompletionAsync(StandardTimeout); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(10, JToken.Parse(status?.Input)); - Assert.AreEqual(3628800, JToken.Parse(status?.Output)); + Assert.AreEqual(10, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(3628800, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -147,8 +147,8 @@ public async Task ParentOfSequentialOrchestration() var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(10, JToken.Parse(status?.Input)); - Assert.AreEqual(3628800, JToken.Parse(status?.Output)); + Assert.AreEqual(10, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(3628800, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -157,7 +157,7 @@ public async Task ParentOfSequentialOrchestration() /// /// End-to-end test which runs a slow orchestrator that causes work item renewal /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LongRunningOrchestrator(bool enableExtendedSessions) @@ -176,7 +176,7 @@ public async Task LongRunningOrchestrator(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(StandardTimeout); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("ok", JToken.Parse(status?.Output)); + Assert.AreEqual("ok", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -266,7 +266,7 @@ public async Task NoInstancesGetAllOrchestrationStatusesNullContinuationToken() } } - [DataTestMethod] + [TestMethod] [DataRow(false, false)] [DataRow(true, false)] [DataRow(false, true)] @@ -281,13 +281,13 @@ public async Task EventConversation(bool enableExtendedSessions, bool useFireAnd var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("OK", JToken.Parse(status?.Output)); + Assert.AreEqual("OK", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } } - [DataTestMethod] + [TestMethod] [DataRow(false)] [DataRow(true)] public async Task AutoStart(bool enableExtendedSessions) @@ -302,13 +302,13 @@ public async Task AutoStart(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("OK", JToken.Parse(status?.Output)); + Assert.AreEqual("OK", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } } - [DataTestMethod] + [TestMethod] [DataRow(false)] [DataRow(true)] public async Task ContinueAsNewThenTimer(bool enableExtendedSessions) @@ -321,7 +321,7 @@ public async Task ContinueAsNewThenTimer(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("OK", JToken.Parse(status?.Output)); + Assert.AreEqual("OK", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -396,7 +396,7 @@ public async Task ValidateCustomStatusPersists() var state = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, state?.OrchestrationStatus); - Assert.AreEqual(customStatus, JToken.Parse(state?.Status)); + Assert.AreEqual(customStatus, JToken.Parse(state?.Status).Value()); await host.StopAsync(); } @@ -956,7 +956,7 @@ public async Task PurgeInstanceHistoryWithoutTimeoutReturnsNullIsComplete() /// End-to-end test which validates parallel function execution by enumerating all files in the current directory /// in parallel and getting the sum total of all file sizes. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ParallelOrchestration(bool enableExtendedSessions) @@ -969,14 +969,14 @@ public async Task ParallelOrchestration(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(90)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(Environment.CurrentDirectory, JToken.Parse(status?.Input)); + Assert.AreEqual(Environment.CurrentDirectory, JToken.Parse(status?.Input).Value()); Assert.IsTrue(long.Parse(status?.Output) > 0L); await host.StopAsync(); } } - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeFanOutOrchestration(bool enableExtendedSessions) @@ -1015,7 +1015,7 @@ public async Task FanOutOrchestration_LargeHistoryBatches() /// /// End-to-end test which validates the ContinueAsNew functionality by implementing a counter actor pattern. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ActorOrchestration(bool enableExtendedSessions) @@ -1051,10 +1051,10 @@ public async Task ActorOrchestration(bool enableExtendedSessions) status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(3, JToken.Parse(status?.Output)); + Assert.AreEqual(3, JToken.Parse(status?.Output).Value()); // When using ContinueAsNew, the original input is discarded and replaced with the most recent state. - Assert.AreNotEqual(initialValue, JToken.Parse(status?.Input)); + Assert.AreNotEqual(initialValue, JToken.Parse(status?.Input).Value()); await host.StopAsync(); } @@ -1063,7 +1063,7 @@ public async Task ActorOrchestration(bool enableExtendedSessions) /// /// End-to-end test which validates the ContinueAsNew functionality by implementing character counter actor pattern. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ActorOrchestrationForLargeInput(bool enableExtendedSessions) @@ -1074,7 +1074,7 @@ public async Task ActorOrchestrationForLargeInput(bool enableExtendedSessions) /// /// End-to-end test which validates the deletion of all data generated by the ContinueAsNew functionality in the character counter actor pattern. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ActorOrchestrationDeleteAllLargeMessageBlobs(bool enableExtendedSessions) @@ -1188,7 +1188,7 @@ private async Task> ValidateCharacterCoun /// /// End-to-end test which validates the Terminate functionality. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task TerminateOrchestration(bool enableExtendedSessions) @@ -1218,7 +1218,7 @@ public async Task TerminateOrchestration(bool enableExtendedSessions) /// /// End-to-end test which validates the Suspend-Resume functionality. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task SuspendResumeOrchestration(bool enableExtendedSessions) @@ -1242,13 +1242,13 @@ public async Task SuspendResumeOrchestration(bool enableExtendedSessions) // Test case 2: external event does not go through await client.RaiseEventAsync("changeStatusNow", changedStatus); status = await client.GetStatusAsync(); - Assert.AreEqual(originalStatus, JToken.Parse(status?.Status)); + Assert.AreEqual(originalStatus, JToken.Parse(status?.Status).Value()); // Test case 3: external event now goes through await client.ResumeAsync("wakeUp"); status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(changedStatus, JToken.Parse(status?.Status)); + Assert.AreEqual(changedStatus, JToken.Parse(status?.Status).Value()); await host.StopAsync(); } @@ -1257,7 +1257,7 @@ public async Task SuspendResumeOrchestration(bool enableExtendedSessions) /// /// Test that a suspended orchestration can be terminated. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task TerminateSuspendedOrchestration(bool enableExtendedSessions) @@ -1286,7 +1286,7 @@ public async Task TerminateSuspendedOrchestration(bool enableExtendedSessions) /// Test that a pending orchestration can be terminated (including tests with a large termination reason that will need to be /// stored in blob storage). /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -1593,7 +1593,7 @@ public async Task RewindNestedSubOrchestrationTest() } } - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task TimerCancellation(bool enableExtendedSessions) @@ -1612,7 +1612,7 @@ public async Task TimerCancellation(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("Approved", JToken.Parse(status?.Output)); + Assert.AreEqual("Approved", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -1621,7 +1621,7 @@ public async Task TimerCancellation(bool enableExtendedSessions) /// /// End-to-end test which validates the handling of durable timer expiration. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task TimerExpiration(bool enableExtendedSessions) @@ -1641,13 +1641,13 @@ public async Task TimerExpiration(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(20)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("Expired", JToken.Parse(status?.Output)); + Assert.AreEqual("Expired", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } } - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task TimerDelay(bool useUtc) @@ -1675,7 +1675,7 @@ public async Task TimerDelay(bool useUtc) } } - [DataTestMethod] + [TestMethod] [DataRow(false)] [DataRow(true)] public async Task OrchestratorStartAtAcceptsAllDateTimeKinds(bool useUtc) @@ -1715,7 +1715,7 @@ public async Task OrchestratorStartAtAcceptsAllDateTimeKinds(bool useUtc) /// /// End-to-end test which validates that orchestrations run concurrently of each other (up to 100 by default). /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task OrchestrationConcurrency(bool enableExtendedSessions) @@ -1754,7 +1754,7 @@ public async Task OrchestrationConcurrency(bool enableExtendedSessions) /// /// End-to-end test which validates the orchestrator's exception handling behavior. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task HandledActivityException(bool enableExtendedSessions) @@ -1768,7 +1768,7 @@ public async Task HandledActivityException(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(15)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(5, JToken.Parse(status?.Output)); + Assert.AreEqual(5, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -1777,7 +1777,7 @@ public async Task HandledActivityException(bool enableExtendedSessions) /// /// End-to-end test which validates the handling of unhandled exceptions generated from orchestrator code. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task UnhandledOrchestrationException(bool enableExtendedSessions) @@ -1800,7 +1800,7 @@ public async Task UnhandledOrchestrationException(bool enableExtendedSessions) /// /// End-to-end test which validates the handling of unhandled exceptions generated from activity code. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task UnhandledActivityException(bool enableExtendedSessions) @@ -1823,7 +1823,7 @@ public async Task UnhandledActivityException(bool enableExtendedSessions) /// /// Fan-out/fan-in test which ensures each operation is run only once. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task FanOutToTableStorage(bool enableExtendedSessions) @@ -1867,7 +1867,7 @@ public void ValidateEventSource() /// /// End-to-end test which validates that orchestrations with <=60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task SmallTextMessagePayloads(bool enableExtendedSessions) @@ -1894,7 +1894,7 @@ public async Task SmallTextMessagePayloads(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(60)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Output)); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -1903,7 +1903,7 @@ public async Task SmallTextMessagePayloads(bool enableExtendedSessions) /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessions) @@ -1920,8 +1920,8 @@ public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessi var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Output)); - Assert.AreEqual(message, JToken.Parse(status.Input)); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); + Assert.AreEqual(message, JToken.Parse(status.Input).Value()); await host.StopAsync(); } @@ -1930,7 +1930,7 @@ public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessi /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeTableTextMessagePayloads_SizeViolation_BlobUrl(bool enableExtendedSessions) @@ -2014,7 +2014,7 @@ public async Task TagsAreAvailableInOrchestrationState() /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeOverallTextMessagePayloads_BlobUrl(bool enableExtendedSessions) @@ -2051,7 +2051,7 @@ await ValidateLargeMessageBlobUrlAsync( /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtendedSessions) @@ -2065,8 +2065,8 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtende var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Input)); - Assert.AreEqual(message, JToken.Parse(status?.Output)); + Assert.AreEqual(message, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2075,7 +2075,7 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtende /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeTableTextMessagePayloads_FetchLargeMessages(bool enableExtendedSessions) @@ -2091,8 +2091,8 @@ public async Task LargeTableTextMessagePayloads_FetchLargeMessages(bool enableEx var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Input)); - Assert.AreEqual(message, JToken.Parse(status?.Output)); + Assert.AreEqual(message, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2127,7 +2127,7 @@ public async Task LargeOrchestrationTags() /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bool enableExtendedSessions) @@ -2141,8 +2141,8 @@ public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bo var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Input)); - Assert.AreEqual(message, JToken.Parse(status?.Output)); + Assert.AreEqual(message, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2151,7 +2151,7 @@ public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bo /// /// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool enableExtendedSessions) @@ -2168,8 +2168,8 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool en status = (await client.GetStateAsync(status.OrchestrationInstance.InstanceId)).First(); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(message, JToken.Parse(status?.Input)); - Assert.AreEqual(message, JToken.Parse(status?.Output)); + Assert.AreEqual(message, JToken.Parse(status?.Input).Value()); + Assert.AreEqual(message, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2179,7 +2179,7 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool en /// End-to-end test which validates that exception messages that are considered valid Urls in the Uri.TryCreate() method /// are handled with an additional Uri format check /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeTextMessagePayloads_URIFormatCheck(bool enableExtendedSessions) @@ -2243,7 +2243,7 @@ private StringBuilder GenerateMediumRandomStringPayload(int numChars = 128 * 102 /// /// End-to-end test which validates that orchestrations with > 60KB binary bytes message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeBinaryByteMessagePayloads(bool enableExtendedSessions) @@ -2273,7 +2273,7 @@ public async Task LargeBinaryByteMessagePayloads(bool enableExtendedSessions) /// /// End-to-end test which validates that orchestrations with > 60KB binary string message sizes can run successfully. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task LargeBinaryStringMessagePayloads(bool enableExtendedSessions) @@ -2305,7 +2305,7 @@ public async Task LargeBinaryStringMessagePayloads(bool enableExtendedSessions) /// /// End-to-end test which validates that a completed singleton instance can be recreated. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task RecreateCompletedInstance(bool enableExtendedSessions) @@ -2323,8 +2323,8 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions) var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("One", JToken.Parse(status?.Input)); - Assert.AreEqual("Hello, One!", JToken.Parse(status?.Output)); + Assert.AreEqual("One", JToken.Parse(status?.Input).Value()); + Assert.AreEqual("Hello, One!", JToken.Parse(status?.Output).Value()); client = await host.StartOrchestrationAsync( typeof(Orchestrations.SayHelloWithActivity), @@ -2333,8 +2333,8 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions) status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("Two", JToken.Parse(status?.Input)); - Assert.AreEqual("Hello, Two!", JToken.Parse(status?.Output)); + Assert.AreEqual("Two", JToken.Parse(status?.Input).Value()); + Assert.AreEqual("Hello, Two!", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2343,7 +2343,7 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions) /// /// End-to-end test which validates that a failed singleton instance can be recreated. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task RecreateFailedInstance(bool enableExtendedSessions) @@ -2369,7 +2369,7 @@ public async Task RecreateFailedInstance(bool enableExtendedSessions) status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual("Hello, NotNull!", JToken.Parse(status?.Output)); + Assert.AreEqual("Hello, NotNull!", JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2378,7 +2378,7 @@ public async Task RecreateFailedInstance(bool enableExtendedSessions) /// /// End-to-end test which validates that a terminated orchestration can be recreated. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task RecreateTerminatedInstance(bool enableExtendedSessions) @@ -2422,7 +2422,7 @@ public async Task RecreateTerminatedInstance(bool enableExtendedSessions) /// /// End-to-end test which validates that a running orchestration can be recreated. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task RecreateRunningInstance(bool enableExtendedSessions) @@ -2509,7 +2509,7 @@ public async Task ExtendedSessions_SessionTimeout() status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(1, JToken.Parse(status?.Output)); + Assert.AreEqual(1, JToken.Parse(status?.Output).Value()); await host.StopAsync(); } @@ -2519,7 +2519,7 @@ public async Task ExtendedSessions_SessionTimeout() /// Tests an orchestration that does two consecutive fan-out, fan-ins. /// This is a regression test for https://github.com/Azure/durabletask/issues/241. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task DoubleFanOut(bool enableExtendedSessions) @@ -2567,7 +2567,7 @@ private static async Task ValidateLargeMessageBlobUrlAsync(string taskHubName, s /// /// Tests the behavior of from orchestrations and activities. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions) @@ -2582,7 +2582,7 @@ public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions) Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); Assert.IsNotNull(status.Output); - Assert.AreEqual("True", JToken.Parse(status.Output)); + Assert.AreEqual("True", JToken.Parse(status.Output).Value()); await host.StopAsync(); } } @@ -2592,7 +2592,7 @@ public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions) /// /// /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ScheduledStart_Inline(bool enableExtendedSessions) @@ -2611,11 +2611,11 @@ public async Task ScheduledStart_Inline(bool enableExtendedSessions) await Task.WhenAll(statusStartingNow, statusStartingIn30Seconds); Assert.AreEqual(OrchestrationStatus.Completed, statusStartingNow.Result?.OrchestrationStatus); - Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input)); + Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input).Value()); Assert.IsNull(statusStartingNow.Result.ScheduledStartTime); Assert.AreEqual(OrchestrationStatus.Completed, statusStartingIn30Seconds.Result?.OrchestrationStatus); - Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input)); + Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input).Value()); Assert.AreEqual(expectedStartTime, statusStartingIn30Seconds.Result.ScheduledStartTime); var startNowResult = (DateTime)JToken.Parse(statusStartingNow.Result?.Output); @@ -2633,7 +2633,7 @@ public async Task ScheduledStart_Inline(bool enableExtendedSessions) /// /// /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ScheduledStart_Activity(bool enableExtendedSessions) @@ -2652,11 +2652,11 @@ public async Task ScheduledStart_Activity(bool enableExtendedSessions) await Task.WhenAll(statusStartingNow, statusStartingIn30Seconds); Assert.AreEqual(OrchestrationStatus.Completed, statusStartingNow.Result?.OrchestrationStatus); - Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input)); + Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input).Value()); Assert.IsNull(statusStartingNow.Result.ScheduledStartTime); Assert.AreEqual(OrchestrationStatus.Completed, statusStartingIn30Seconds.Result?.OrchestrationStatus); - Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input)); + Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input).Value()); Assert.AreEqual(expectedStartTime, statusStartingIn30Seconds.Result.ScheduledStartTime); var startNowResult = (DateTime)JToken.Parse(statusStartingNow.Result?.Output); @@ -2674,7 +2674,7 @@ public async Task ScheduledStart_Activity(bool enableExtendedSessions) /// /// /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ScheduledStart_Activity_GetStatus_Returns_ScheduledStart(bool enableExtendedSessions) @@ -2719,7 +2719,7 @@ await Task.WhenAll( /// To recover from this, users may set `AllowReplayingTerminalInstances` to true. When this is set, DTFx will not discard /// events for terminal orchestrators, forcing a replay which eventually updates the instances table to the right state. /// - [DataTestMethod] + [TestMethod] [DataRow(true, true, true)] [DataRow(true, true, false)] [DataRow(true, false, true)] @@ -2817,7 +2817,7 @@ public async Task TestAllowReplayingTerminalInstances(bool enableExtendedSession /// the tracking store context object that is part of the orchestration session state which keeps track of the blobs. /// /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -2909,7 +2909,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallCompletedOrchestrat /// Same as but for a failed orchestration. /// /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3001,7 +3001,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallFailedOrchestration /// /// Same as but for a terminated orchestration. /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3095,7 +3095,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallTerminatedOrchestra /// Same as but for an orchestration with large input /// and output, which will need to be stored in blob storage. /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3188,7 +3188,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallLargeInputOutput(bo /// Same as but for a large termination reason that /// will need to be stored in blob storage. /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3285,7 +3285,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallLargeTerminationRea /// Same as but for a large exception message that will need /// to be stored in blob storage. /// - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3477,7 +3477,7 @@ public async Task OrchestrationRejectsWithVersionMismatch() /// /// The value to use for /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task WorkerAttemptingToUpdateInstanceTableAfterStalling(bool useInstanceEtag) @@ -3541,7 +3541,7 @@ await service.CreateTaskOrchestrationAsync( if (useInstanceEtag) { // Confirm an exception is thrown due to the etag mismatch for the instance table when the worker attempts to complete the work item - SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () => + SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () => await service.CompleteTaskOrchestrationWorkItemAsync(workItem, runtimeState, new List(), new List(), new List(), null, null) ); Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException)); @@ -3592,7 +3592,7 @@ await service.CreateTaskOrchestrationAsync( /// /// The value to use for /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task WorkerAttemptingToUpdateInstanceTableAfterStallingForSubOrchestration(bool useInstanceEtag) @@ -3699,7 +3699,7 @@ await service.CreateTaskOrchestrationAsync( { // Confirm an exception is thrown because the worker attempts to insert a new entity for the suborchestration into the instance table // when one already exists - SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () => + SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () => await service.CompleteTaskOrchestrationWorkItemAsync(workItem, runtimeState, new List(), new List(), new List(), null, null) ); Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException)); @@ -3733,7 +3733,7 @@ await service.CreateTaskOrchestrationAsync( } } - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task WorkerAttemptingToDequeueMessageForNonExistentInstance(bool extendedSessionsEnabled) @@ -3785,7 +3785,7 @@ await service.SendTaskOrchestrationMessageAsync( } } - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3878,7 +3878,7 @@ await service.SendTaskOrchestrationMessageAsync( } } - [DataTestMethod] + [TestMethod] [DataRow(true, true)] [DataRow(false, true)] [DataRow(true, false)] @@ -3991,7 +3991,7 @@ await service.SendTaskOrchestrationMessageAsync( /// End-to-end test which validates a simple orchestrator function that calls an activity function /// and checks the OpenTelemetry trace information /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task OpenTelemetry_SayHelloWithActivity(bool enableExtendedSessions) @@ -4061,7 +4061,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 /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task OpenTelemetry_ExternalEvent_RaiseEvent(bool enableExtendedSessions) @@ -4131,7 +4131,7 @@ public async Task OpenTelemetry_ExternalEvent_RaiseEvent(bool enableExtendedSess /// /// End-to-end test which validates a simple orchestrator function that fires a timer and checks the OpenTelemetry trace information /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task OpenTelemetry_TimerFired(bool enableExtendedSessions) @@ -4198,7 +4198,7 @@ public async Task OpenTelemetry_TimerFired(bool enableExtendedSessions) /// 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 /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task OpenTelemetry_ExternalEvent_SendEvent(bool enableExtendedSessions) diff --git a/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs b/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs index b9a41b5c5..694d627af 100644 --- a/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs +++ b/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs @@ -21,7 +21,7 @@ public class AzureTableQueryFilterTests { // PartitionKeyEquals applies KeySanitation.EscapePartitionKey (storage-key characters) and then // OData quote-escaping (single quotes doubled). - [DataTestMethod] + [TestMethod] [DataRow("instance1", "PartitionKey eq 'instance1'")] [DataRow("inst'ance", "PartitionKey eq 'inst''ance'")] [DataRow("in#st'ance", "PartitionKey eq 'in^2st''ance'")] @@ -31,7 +31,7 @@ public void PartitionKeyEquals(string instanceId, string expectedFilter) } // ColumnEquals OData-escapes the value (single quotes doubled); the column name is literal text. - [DataTestMethod] + [TestMethod] [DataRow("ExecutionId", "abc", "ExecutionId eq 'abc'")] [DataRow("ExecutionId", "a'b", "ExecutionId eq 'a''b'")] [DataRow("RowKey", "", "RowKey eq ''")] @@ -40,7 +40,7 @@ public void ColumnEquals(string columnName, string value, string expectedFilter) Assert.AreEqual(expectedFilter, AzureTableQueryFilter.ColumnEquals(columnName, value)); } - [DataTestMethod] + [TestMethod] [DataRow("prefix", "PartitionKey ge 'prefix'")] [DataRow("pre'fix", "PartitionKey ge 'pre''fix'")] public void PartitionKeyGreaterOrEqual(string sanitizedPartitionKey, string expectedFilter) @@ -48,7 +48,7 @@ public void PartitionKeyGreaterOrEqual(string sanitizedPartitionKey, string expe Assert.AreEqual(expectedFilter, AzureTableQueryFilter.PartitionKeyGreaterOrEqual(sanitizedPartitionKey)); } - [DataTestMethod] + [TestMethod] [DataRow("prefix", "PartitionKey lt 'prefix'")] [DataRow("pre'fix", "PartitionKey lt 'pre''fix'")] public void PartitionKeyLessThan(string sanitizedPartitionKey, string expectedFilter) diff --git a/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs b/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs index 91643511d..758c76745 100644 --- a/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs +++ b/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs @@ -31,7 +31,7 @@ namespace DurableTask.AzureStorage.Tests.Correlation [TestClass] public class CorrelationScenarioTest { - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -78,7 +78,7 @@ protected override string Execute(TaskContext context, string input) } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -113,7 +113,7 @@ public async Task SingleOrchestrationWithThrowingExceptionAsync(Protocol protoco ); } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -173,7 +173,7 @@ protected override async Task ExecuteAsync(TaskContext context, string i } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -217,7 +217,7 @@ public override Task RunTask(OrchestrationContext context, string input) } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -274,7 +274,7 @@ public override async Task RunTask(OrchestrationContext context, string } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -338,7 +338,7 @@ protected override string Execute(TaskContext context, string input) } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -447,7 +447,7 @@ protected override string Execute(TaskContext context, string input) //[TestMethod] ContinueAsNew - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -502,7 +502,7 @@ internal static void Reset() } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -576,7 +576,7 @@ internal static void Reset() } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] @@ -718,7 +718,7 @@ internal static void Reset() } } - [DataTestMethod] + [TestMethod] [DataRow(Protocol.W3CTraceContext, false)] [DataRow(Protocol.HttpCorrelationProtocol, false)] [DataRow(Protocol.W3CTraceContext, true)] diff --git a/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs b/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs index 164e7ebc0..2ba468c19 100644 --- a/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs +++ b/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs @@ -35,7 +35,7 @@ public void TestParseTraceParent() public void TestParseTraceParentThrowsException() { string wrongTraceparentString = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7"; - Assert.ThrowsException( + Assert.ThrowsExactly( () => { wrongTraceparentString.ToTraceParent(); }); } diff --git a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj index c1b403fe6..dda3a488c 100644 --- a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj +++ b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj @@ -10,11 +10,6 @@ - @@ -34,6 +29,12 @@ Since this is just a test project, and to prevent "warning fatigue" so real CVEs stand out, we choose to upgrade the dependency explicitly to remove the false alarm.--> + + + + + + @@ -51,10 +52,6 @@ - - - - Always diff --git a/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs b/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs index 326ffc1e3..d631241ac 100644 --- a/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs +++ b/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs @@ -24,7 +24,7 @@ namespace DurableTask.AzureStorage.Tests [TestClass] public class KeySanitationTests { - [DataTestMethod] + [TestMethod] [DataRow("\r")] [DataRow("")] [DataRow("hello")] diff --git a/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs b/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs index 008a9eac7..e16d13526 100644 --- a/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs +++ b/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs @@ -23,7 +23,7 @@ namespace DurableTask.AzureStorage.Tests [TestClass] public class MessageManagerTests { - [DataTestMethod] + [TestMethod] [DataRow("System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.String, System.Private.CoreLib]]")] [DataRow("System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]]")] public void DeserializesStandardTypes(string dictionaryType) @@ -49,7 +49,7 @@ public void FailsDeserializingUnknownTypes() var messageManager = SetupMessageManager(new KnownTypeBinder()); // When/Then - Assert.ThrowsException(() => messageManager.DeserializeMessageData(message)); + Assert.ThrowsExactly(() => messageManager.DeserializeMessageData(message)); } @@ -69,7 +69,7 @@ public void DeserializesCustomTypes() Assert.AreEqual("tagValue", startedEvent.Tags["tag1"]); } - [DataTestMethod] + [TestMethod] [DataRow("blob.bin", "blob.bin")] [DataRow("@#$%!", "%40%23%24%25%21")] [DataRow("foo/bar/b@z.tar.gz", "foo/bar/b%40z.tar.gz")] diff --git a/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs b/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs index 78703d5fd..4f528070a 100644 --- a/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs +++ b/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs @@ -18,7 +18,7 @@ namespace DurableTask.AzureStorage.Net [TestClass] public class UriPathTests { - [DataTestMethod] + [TestMethod] [DataRow("", "", "")] [DataRow("", "bar/baz", "bar/baz")] [DataRow("foo", "", "foo")] diff --git a/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs b/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs index 87a73edfa..b7fd9bc0b 100644 --- a/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs +++ b/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs @@ -250,7 +250,7 @@ public async Task DeleteBatchParallelAsync_CancellationToken_IsPropagated() return Task.FromResult(CreateMockBatchResponse(batch.Count())); }); - await Assert.ThrowsExceptionAsync( + await Assert.ThrowsExactlyAsync( () => table.DeleteBatchParallelAsync(entities, cts.Token)); } diff --git a/test/DurableTask.AzureStorage.Tests/StressTests.cs b/test/DurableTask.AzureStorage.Tests/StressTests.cs index 101dc6b51..9bdbf5542 100644 --- a/test/DurableTask.AzureStorage.Tests/StressTests.cs +++ b/test/DurableTask.AzureStorage.Tests/StressTests.cs @@ -49,7 +49,7 @@ public void Cleanup() /// both in the case where they all share the same instance ID and when they have unique /// instance IDs. /// - [DataTestMethod] + [TestMethod] [DataRow(true)] [DataRow(false)] public async Task ConcurrentOrchestrationStarts(bool useSameInstanceId) diff --git a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs index 7fdb42e04..e581c7acc 100644 --- a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs +++ b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs @@ -702,7 +702,7 @@ await WaitForConditionAsync( // read the partition table var results = partitionTable.ExecuteQueryAsync(); var numResults = await results.CountAsync(); - Assert.AreEqual(numResults, 1); // there should only be 1 partition + Assert.AreEqual(1, numResults); // there should only be 1 partition // We want to test that worker 0 starts listening to the control queue without claiming the lease. // Therefore, we force the table to be in a state where worker 0 is still the current owner of the partition. @@ -716,8 +716,8 @@ await WaitForConditionAsync( // guarantee table is corrrectly updated results = partitionTable.ExecuteQueryAsync(); numResults = await results.CountAsync(); - Assert.AreEqual(numResults, 1); // there should only be 1 partition - Assert.AreEqual((await results.FirstAsync()).CurrentOwner, "0"); // ensure current owner is partition "0" + Assert.AreEqual(1, numResults); // there should only be 1 partition + Assert.AreEqual("0", (await results.FirstAsync()).CurrentOwner); // ensure current owner is partition "0" // create and start new worker with the same settings, ensure it is actively listening to the queue worker = new TaskHubWorker(service); diff --git a/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs b/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs index cfa4e3966..ea40b4001 100644 --- a/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs +++ b/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs @@ -622,12 +622,12 @@ public void Context_ContinueAsNew_LastCallWins() } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] public void Context_ContinueAsNew_NullOptions_Throws() { var instance = new OrchestrationInstance { InstanceId = "test", ExecutionId = Guid.NewGuid().ToString() }; var context = new TestableTaskOrchestrationContext(instance, TaskScheduler.Default); - context.ContinueAsNew(null, "input", (ContinueAsNewOptions)null!); + Assert.ThrowsExactly( + () => context.ContinueAsNew(null, "input", (ContinueAsNewOptions)null!)); } #endregion @@ -635,11 +635,11 @@ public void Context_ContinueAsNew_NullOptions_Throws() #region Base class — NotSupportedException for unsupported implementations [TestMethod] - [ExpectedException(typeof(NotSupportedException))] public void BaseClass_ContinueAsNewWithOptions_ThrowsNotSupported() { var ctx = new MinimalOrchestrationContext(); - ctx.ContinueAsNew("1.0", "input", new ContinueAsNewOptions()); + Assert.ThrowsExactly( + () => ctx.ContinueAsNew("1.0", "input", new ContinueAsNewOptions())); } #endregion diff --git a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs index e91b7ef84..b1ff65076 100644 --- a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs +++ b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs @@ -339,7 +339,7 @@ public async Task EnsureActivityDispatcherMiddlewareHasAccessToRuntimeState() Assert.AreEqual("Value", executionContext?.OrchestrationTags?["Test"]); } - [DataTestMethod] + [TestMethod] [DataRow(OrchestrationStatus.Completed)] [DataRow(OrchestrationStatus.Failed)] [DataRow(OrchestrationStatus.Terminated)] diff --git a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs index d4ab393f9..675ae951a 100644 --- a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs +++ b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs @@ -49,7 +49,7 @@ public ExceptionHandlingIntegrationTests() this.client = new TaskHubClient(service, loggerFactory: loggerFactory); } - [DataTestMethod] + [TestMethod] [DataRow(ErrorPropagationMode.SerializeExceptions)] [DataRow(ErrorPropagationMode.UseFailureDetails)] public async Task CatchInvalidOperationException(ErrorPropagationMode mode) @@ -123,7 +123,7 @@ await this.worker Assert.AreEqual(1, retryPolicyInvokedCount); } - [DataTestMethod] + [TestMethod] [DataRow(ErrorPropagationMode.SerializeExceptions)] [DataRow(ErrorPropagationMode.UseFailureDetails)] public async Task FailureDetailsOnUnhandled(ErrorPropagationMode mode) diff --git a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs index eddd9b12c..d82ad53cc 100644 --- a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs +++ b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs @@ -28,10 +28,10 @@ public async Task Invoke_WithFailingRetryCall_ShouldThrowCorrectException() var interceptor = new RetryInterceptor(this.context, new RetryOptions(TimeSpan.FromMilliseconds(100), 1), () => throw new IOException()); async Task Invoke() => await interceptor.Invoke(); - await Assert.ThrowsExceptionAsync(Invoke, "Interceptor should throw the original exception after exceeding max retry attempts."); + await Assert.ThrowsExactlyAsync(Invoke, "Interceptor should throw the original exception after exceeding max retry attempts."); } - [DataTestMethod] + [TestMethod] [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."); } - [DataTestMethod] + [TestMethod] [DataRow(1)] [DataRow(2)] [DataRow(3)] diff --git a/test/DurableTask.Core.Tests/TraceContextBaseTest.cs b/test/DurableTask.Core.Tests/TraceContextBaseTest.cs index be32719e6..b628e9050 100644 --- a/test/DurableTask.Core.Tests/TraceContextBaseTest.cs +++ b/test/DurableTask.Core.Tests/TraceContextBaseTest.cs @@ -121,7 +121,7 @@ public void GetCurrentOrchestrationRequestTraceContextMultiOrchestratorScenario( public void GetCurrentOrchestrationRequestTraceContextWithNoRequestTraceContextScenario() { TraceContextBase currentContext = new Foo(); - Assert.ThrowsException(() => currentContext.GetCurrentOrchestrationRequestTraceContext()); + Assert.ThrowsExactly(() => currentContext.GetCurrentOrchestrationRequestTraceContext()); } private static Foo GetNewRequestContext(string comment) diff --git a/test/DurableTask.Core.Tests/WorkItemDispatcherTests.cs b/test/DurableTask.Core.Tests/WorkItemDispatcherTests.cs index 0e05f3e07..894c53c10 100644 --- a/test/DurableTask.Core.Tests/WorkItemDispatcherTests.cs +++ b/test/DurableTask.Core.Tests/WorkItemDispatcherTests.cs @@ -407,7 +407,7 @@ public InMemoryLogger(ConcurrentBag logs) #if NET8_0_OR_GREATER public IDisposable? BeginScope(TState state) where TState : notnull => NoOpDisposable.Instance; #else - public IDisposable BeginScope(TState state) => NoOpDisposable.Instance; + public IDisposable BeginScope(TState state) where TState : notnull => NoOpDisposable.Instance; #endif public bool IsEnabled(LogLevel logLevel) => true; diff --git a/test/DurableTask.Emulator.Tests/EmulatorFunctionalTests.cs b/test/DurableTask.Emulator.Tests/EmulatorFunctionalTests.cs index 0ba937bbd..064e9cdf7 100644 --- a/test/DurableTask.Emulator.Tests/EmulatorFunctionalTests.cs +++ b/test/DurableTask.Emulator.Tests/EmulatorFunctionalTests.cs @@ -76,9 +76,9 @@ await worker.AddTaskOrchestrations(typeof(SimplestGreetingsOrchestration)) OrchestrationState result = await client.WaitForOrchestrationAsync(id, TimeSpan.FromSeconds(30), new CancellationToken()); Assert.AreEqual(OrchestrationStatus.Completed, result.OrchestrationStatus); - await Assert.ThrowsExceptionAsync(() => client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null)); + await Assert.ThrowsExactlyAsync(() => client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null)); - await Assert.ThrowsExceptionAsync(() => client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, new[] { OrchestrationStatus.Completed })); + await Assert.ThrowsExactlyAsync(() => client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, new[] { OrchestrationStatus.Completed })); SimplestGreetingsOrchestration.Result = String.Empty; diff --git a/test/DurableTask.ServiceBus.Tests/DurableTask.ServiceBus.Tests.csproj b/test/DurableTask.ServiceBus.Tests/DurableTask.ServiceBus.Tests.csproj index c3bf4e3de..a9194524d 100644 --- a/test/DurableTask.ServiceBus.Tests/DurableTask.ServiceBus.Tests.csproj +++ b/test/DurableTask.ServiceBus.Tests/DurableTask.ServiceBus.Tests.csproj @@ -35,6 +35,7 @@ + diff --git a/test/DurableTask.ServiceBus.Tests/ErrorHandlingTests.cs b/test/DurableTask.ServiceBus.Tests/ErrorHandlingTests.cs index c74beeeae..53b46a26d 100644 --- a/test/DurableTask.ServiceBus.Tests/ErrorHandlingTests.cs +++ b/test/DurableTask.ServiceBus.Tests/ErrorHandlingTests.cs @@ -475,7 +475,7 @@ await this.taskHub.AddTaskOrchestrations(new TestObjectCreator(Action action) try { action(); - Assert.IsTrue(false); + Assert.Fail(); } catch (Exception ex) { diff --git a/test/DurableTask.ServiceBus.Tests/OrchestrationHubTableClientTests.cs b/test/DurableTask.ServiceBus.Tests/OrchestrationHubTableClientTests.cs index afbcf83f9..0d2f5b0c9 100644 --- a/test/DurableTask.ServiceBus.Tests/OrchestrationHubTableClientTests.cs +++ b/test/DurableTask.ServiceBus.Tests/OrchestrationHubTableClientTests.cs @@ -67,13 +67,13 @@ await this.taskHub.AddTaskOrchestrations(typeof (InstanceStoreTestOrchestration) bool isCompleted = await TestHelpers.WaitForInstanceAsync(this.client, id, 60); Assert.IsTrue(isCompleted, TestHelpers.GetInstanceNotCompletedMessage(this.client, id, 60)); OrchestrationState runtimeState = await this.client.GetOrchestrationStateAsync(id); - Assert.AreEqual(runtimeState.OrchestrationStatus, OrchestrationStatus.Completed); - Assert.AreEqual(runtimeState.OrchestrationInstance.InstanceId, id.InstanceId); - Assert.AreEqual(runtimeState.OrchestrationInstance.ExecutionId, id.ExecutionId); + Assert.AreEqual(OrchestrationStatus.Completed, runtimeState.OrchestrationStatus); + Assert.AreEqual(id.InstanceId, runtimeState.OrchestrationInstance.InstanceId); + Assert.AreEqual(id.ExecutionId, runtimeState.OrchestrationInstance.ExecutionId); Assert.AreEqual("DurableTask.ServiceBus.Tests.OrchestrationHubTableClientTests+InstanceStoreTestOrchestration", runtimeState.Name); - Assert.AreEqual(runtimeState.Version, string.Empty); - Assert.AreEqual(runtimeState.Input, "\"DONT_THROW\""); - Assert.AreEqual(runtimeState.Output, "\"Spartacus\""); + Assert.AreEqual(string.Empty, runtimeState.Version); + Assert.AreEqual("\"DONT_THROW\"", runtimeState.Input); + Assert.AreEqual("\"Spartacus\"", runtimeState.Output); string history = await this.client.GetOrchestrationHistoryAsync(id); Assert.IsTrue(!string.IsNullOrWhiteSpace(history)); @@ -147,14 +147,14 @@ await this.taskHub.AddTaskOrchestrations(typeof (InstanceStoreTestOrchestration) Assert.AreEqual(id.InstanceId, runtimeState.OrchestrationInstance.InstanceId); Assert.AreEqual(id.ExecutionId, runtimeState.OrchestrationInstance.ExecutionId); Assert.AreEqual("DurableTask.ServiceBus.Tests.OrchestrationHubTableClientTests+InstanceStoreTestOrchestration", runtimeState.Name); - Assert.AreEqual(runtimeState.Version, string.Empty); - Assert.AreEqual(runtimeState.Input, "\"WAIT\""); - Assert.AreEqual(runtimeState.Output, null); + Assert.AreEqual(string.Empty, runtimeState.Version); + Assert.AreEqual("\"WAIT\"", runtimeState.Input); + Assert.IsNull(runtimeState.Output); bool isCompleted = await TestHelpers.WaitForInstanceAsync(this.client, id, 60); Assert.IsTrue(isCompleted, TestHelpers.GetInstanceNotCompletedMessage(this.client, id, 60)); runtimeState = await this.client.GetOrchestrationStateAsync(id); - Assert.AreEqual(runtimeState.OrchestrationStatus, OrchestrationStatus.Completed); + Assert.AreEqual(OrchestrationStatus.Completed, runtimeState.OrchestrationStatus); } [TestMethod] diff --git a/test/DurableTask.ServiceBus.Tests/SampleScenarioTests.cs b/test/DurableTask.ServiceBus.Tests/SampleScenarioTests.cs index 84319c9b6..457015bd0 100644 --- a/test/DurableTask.ServiceBus.Tests/SampleScenarioTests.cs +++ b/test/DurableTask.ServiceBus.Tests/SampleScenarioTests.cs @@ -104,11 +104,11 @@ await this.taskHub.AddTaskOrchestrations(typeof(SimplestGreetingsOrchestration)) Assert.AreEqual("Greeting send to Gabbar", SimplestGreetingsOrchestration.Result, "Orchestration Result is wrong!!!"); - await Assert.ThrowsExceptionAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null)); + await Assert.ThrowsExactlyAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null)); - await Assert.ThrowsExceptionAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, null)); + await Assert.ThrowsExactlyAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, null)); - await Assert.ThrowsExceptionAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, new[] { OrchestrationStatus.Completed, OrchestrationStatus.Terminated })); + await Assert.ThrowsExactlyAsync(() => this.client.CreateOrchestrationInstanceAsync(typeof(SimplestGreetingsOrchestration), id.InstanceId, null, new[] { OrchestrationStatus.Completed, OrchestrationStatus.Terminated })); SimplestGreetingsOrchestration.Result = string.Empty; diff --git a/test/DurableTask.ServiceBus.Tests/ServiceBusOrchestrationServiceTests.cs b/test/DurableTask.ServiceBus.Tests/ServiceBusOrchestrationServiceTests.cs index 0d3418aa9..e58e3e1f3 100644 --- a/test/DurableTask.ServiceBus.Tests/ServiceBusOrchestrationServiceTests.cs +++ b/test/DurableTask.ServiceBus.Tests/ServiceBusOrchestrationServiceTests.cs @@ -110,10 +110,10 @@ await this.taskHub.AddTaskOrchestrations(typeof(CounterOrchestration)) status = await client.WaitForOrchestrationAsync(temp, TimeSpan.FromSeconds(10)); Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus); - Assert.AreEqual(3, JToken.Parse(status?.Output)); + Assert.AreEqual(3, JToken.Parse(status?.Output).Value()); // When using ContinueAsNew, the original input is discarded and replaced with the most recent state. - Assert.AreNotEqual(initialValue, JToken.Parse(status?.Input)); + Assert.AreNotEqual(initialValue, JToken.Parse(status?.Input).Value()); } [TestMethod] diff --git a/test/DurableTask.Stress.Tests/Options.cs b/test/DurableTask.Stress.Tests/Options.cs index df7fe514c..785534953 100644 --- a/test/DurableTask.Stress.Tests/Options.cs +++ b/test/DurableTask.Stress.Tests/Options.cs @@ -18,7 +18,6 @@ namespace DurableTask.Stress.Tests internal class Options { -#if NETCOREAPP [Option('c', "create-hub", Default = false, HelpText = "Create Orchestration Hub.")] public bool CreateHub { get; set; } @@ -48,37 +47,5 @@ public static string GetUsage(ParserResult options) help.AddOptions(options); return help; } -#else - [Option('c', "create-hub", DefaultValue = false, - HelpText = "Create Orchestration Hub.")] - public bool CreateHub { get; set; } - - [Option('s', "start-instance", DefaultValue = null, - HelpText = "Start Driver Instance")] - public string StartInstance { get; set; } - - [Option('i', "instance-id", - HelpText = "Instance id for new orchestration instance.")] - public string InstanceId { get; set; } - - [HelpOption] - public string GetUsage() - { - // this without using CommandLine.Text - // or using HelpText.AutoBuild - - var help = new HelpText - { - Heading = new HeadingInfo("TaskHubStressTest", "1.0"), - AdditionalNewLineAfterOption = true, - AddDashesToOption = true - }; - help.AddPreOptionsLine("Usage: TaskHubStressTest.exe -c"); - help.AddPreOptionsLine("Usage: TaskHubStressTest.exe -c -s "); - help.AddPreOptionsLine("Usage: TaskHubStressTest.exe -i "); - help.AddOptions(this); - return help; - } -#endif } } diff --git a/test/DurableTask.Stress.Tests/Program.cs b/test/DurableTask.Stress.Tests/Program.cs index 654acc78e..dd266654e 100644 --- a/test/DurableTask.Stress.Tests/Program.cs +++ b/test/DurableTask.Stress.Tests/Program.cs @@ -150,6 +150,7 @@ namespace DurableTask.Stress.Tests using System.Configuration; using System.Diagnostics; using System.Diagnostics.Tracing; + using CommandLine; using DurableTask.AzureStorage; using DurableTask.Core; using DurableTask.Core.Tracing; @@ -158,7 +159,6 @@ namespace DurableTask.Stress.Tests internal class Program { - static readonly Options ArgumentOptions = new Options(); static ObservableEventListener eventListener; // ReSharper disable once UnusedMember.Local @@ -170,7 +170,13 @@ static void Main(string[] args) string tableConnectionString = ConfigurationManager.AppSettings["StorageConnectionString"]; - if (CommandLine.Parser.Default.ParseArgumentsStrict(args, ArgumentOptions)) + Options argumentOptions = null; + ParserResult parserResult = Parser.Default.ParseArguments(args); + parserResult + .WithParsed(options => argumentOptions = options) + .WithNotParsed(errors => Console.Error.WriteLine(Options.GetUsage(parserResult))); + + if (argumentOptions != null) { string connectionString = ConfigurationManager.ConnectionStrings["AzureStorage"].ConnectionString; var settings = new AzureStorageOrchestrationServiceSettings @@ -186,13 +192,13 @@ static void Main(string[] args) var taskHubClient = new TaskHubClient(orchestrationServiceAndClient); var taskHub = new TaskHubWorker(orchestrationServiceAndClient); - if (ArgumentOptions.CreateHub) + if (argumentOptions.CreateHub) { orchestrationServiceAndClient.CreateIfNotExistsAsync().Wait(); } OrchestrationInstance instance; - string instanceId = ArgumentOptions.StartInstance; + string instanceId = argumentOptions.StartInstance; if (!string.IsNullOrWhiteSpace(instanceId)) { @@ -212,7 +218,7 @@ static void Main(string[] args) } else { - instance = new OrchestrationInstance { InstanceId = ArgumentOptions.InstanceId }; + instance = new OrchestrationInstance { InstanceId = argumentOptions.InstanceId }; } Console.WriteLine($"Orchestration starting: {DateTime.Now}"); diff --git a/test/TestFabricApplication/TestApplication/TestApplication.sfproj b/test/TestFabricApplication/TestApplication/TestApplication.sfproj index c9dbc1e05..5aa05c44b 100644 --- a/test/TestFabricApplication/TestApplication/TestApplication.sfproj +++ b/test/TestFabricApplication/TestApplication/TestApplication.sfproj @@ -1,11 +1,11 @@  - + 1d52f94e-933c-411f-96c4-4960b423586f 2.4 1.5 - 1.6.7 + 1.7.9 v4.7.2 @@ -39,9 +39,9 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Service Fabric Tools\Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets - + - - + + \ No newline at end of file diff --git a/test/TestFabricApplication/TestApplication/packages.config b/test/TestFabricApplication/TestApplication/packages.config index 33a2f1446..ece21af86 100644 --- a/test/TestFabricApplication/TestApplication/packages.config +++ b/test/TestFabricApplication/TestApplication/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file