public async Task ConnectWorker_FollowsFailureOptionsIfTheWorkerFuncDoesntTakeAction()
        {
            await ConnectAsync();

            await prod.UseAsync("watched");

            await DrainUsedTube();

            int receivedJobId = 0;
            var options       = new WorkerOptions {
                Tubes = { "watched" }, FailureBehavior = WorkerFailureBehavior.Bury
            };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async(c, job) =>
            {
                if (receivedJobId == 0)
                {
                    receivedJobId = job.Id;
                }
                await Task.Delay(0); // Don't call any IWorker methods
            });

            using (await worker)
            {
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

                await Task.Delay(100);
            }

            var stat = await prod.JobStatisticsAsync(receivedJobId);

            Assert.Equal(JobState.Buried, stat.State);
        }
Ejemplo n.º 2
0
 public ItemSyncJob(IStockItemRepository stockRepository, ICrmRepository crmRepository,
                    IOptions <WorkerOptions> options)
 {
     _crmRepository   = crmRepository ?? throw new ArgumentNullException(nameof(crmRepository));
     _stockRepository = stockRepository ?? throw new ArgumentNullException(nameof(stockRepository));
     _options         = options?.Value ?? throw new ArgumentNullException(nameof(options));
 }
        public async Task ConnectWorker_StopsWhenDisposed()
        {
            await ConnectAsync();

            var tube = "i-am-a-tube";

            int counter = 0;
            var options = new WorkerOptions {
                Tubes = { tube }
            };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async(c, job) =>
            {
                counter++;
                await c.DeleteAsync();
            });

            using (await worker)
            {
                await prod.UseAsync(tube);

                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

                await Task.Delay(200);
            }

            Assert.True(counter > 0);
            var finalValue = counter;

            await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

            await Task.Delay(200);

            Assert.Equal(finalValue, counter);
        }
Ejemplo n.º 4
0
        public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureHostConfiguration(BuildConfiguration)

        .UseServiceProviderFactory(new AutofacServiceProviderFactory())

        .ConfigureContainer <ContainerBuilder>(builder =>
        {
            builder.RegisterType <SecurityHelper>();
            builder.RegisterType <SqlConnectionHelper>().As <ISqlConnectionHelper>();
            builder.RegisterType <QueryBuilderService>().As <IQueryBuilderService>();
            builder.RegisterType <DapperQueryService>().As <IDapperQueryService>();
            builder.RegisterType <RabbitMqListenerService>().As <IRabbitMqListenerService>();
        })

        .ConfigureServices((hostContext, services) =>
        {
            IConfiguration configuration = hostContext.Configuration;

            WorkerOptions workerOptions = configuration.GetSection("WorkerOptions").Get <WorkerOptions>();
            services.AddSingleton(workerOptions);

            services.AddHostedService <Worker>();

            services.AddDbContext <QueryBuilderDbContext>(options => options
                                                          .UseSqlServer(configuration.GetValue <string>("ConnectionString"),
                                                                        providerOptions => providerOptions.CommandTimeout(configuration.GetValue <int>("ConnectionStringTimeOut")))
                                                          .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
        })
        .UseWindowsService()
        .UseSerilog();
        public async Task ConnectWorker_CallsTheWorkerOnTheCurrentSyncContextByDefault()
        {
            await ConnectAsync();

            const int MESSAGE_COUNT                = 10;
            int       wrongContextCount            = 0;
            SynchronizationContext startingContext = SynchronizationContext.Current;
            var options   = new WorkerOptions {
            };
            var countdown = new CountdownEvent(MESSAGE_COUNT);
            var worker    = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async(c, job) =>
            {
                countdown.Signal();
                if (startingContext != SynchronizationContext.Current)
                {
                    wrongContextCount++;
                }
                await c.DeleteAsync();
            });

            using (await worker)
            {
                foreach (var n in Enumerable.Repeat(0, MESSAGE_COUNT))
                {
                    await prod.PutAsync(BitConverter.GetBytes(n), 1, TenSeconds, ZeroSeconds);
                }
                var consumedAllMessages = countdown.Wait(TimeSpan.FromSeconds(10));
                Assert.True(consumedAllMessages, $"Did not consume all messages, remaining: {countdown.CurrentCount}");
            }

            Assert.Equal(0, wrongContextCount);
        }
Ejemplo n.º 6
0
 public YahooService(WorkerOptions workerOptions)
 {
     _client             = new HttpClient();
     _client.BaseAddress = new Uri(workerOptions.YahooFinanceApi.ApiUrl);
     _client.DefaultRequestHeaders.Add("x-rapidapi-key", workerOptions.YahooFinanceApi.ApiKey);
     _client.DefaultRequestHeaders.Add("x-rapidapi-host", workerOptions.YahooFinanceApi.ApiHost);
 }
Ejemplo n.º 7
0
 public TcpServer(KeyEventProcessor keyEventProcessor, WorkerOptions options, ILogger <TcpServer> logger)
 {
     _keyEventMessageProcessor = keyEventProcessor;
     _logger     = logger;
     _port       = options.Port;
     _ipTemplate = options.IpTemplate;
 }
Ejemplo n.º 8
0
    //-------------------------------------------------------------------------
    /// <summary>
    /// Executes the conversion of test-files asynchronously.
    /// </summary>
    /// <param name="options">The <see cref="WorkerOptions"/> to use for the conversion.</param>
    /// <returns>A task that completes when the conversion is finished.</returns>
    /// <exception cref="ArgumentNullException">
    /// Thrown when <paramref name="options"/> is <c>null</c>.
    /// </exception>
    public async Task RunAsync(WorkerOptions options)
    {
#if NET6_0_OR_GREATER
        ArgumentNullException.ThrowIfNull(options);
#else
        if (options is null)
        {
            throw new ArgumentNullException(nameof(options));
        }
#endif

        Thread.CurrentThread.CurrentCulture   = CultureInfo.InvariantCulture;
        Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

        _globHandler.ExpandWildcards(options);

        ITestResultXmlConverter converter;

        if (options.ConvertToJunit)
        {
            converter = new Trx2JunitTestResultXmlConverter();
            Console.WriteLine($"Converting {options.InputFiles.Count} trx file(s) to JUnit-xml...");
        }
        else
        {
            converter = new Junit2TrxTestResultXmlConverter();
            Console.WriteLine($"Converting {options.InputFiles.Count} junit file(s) to trx-xml...");
        }
        DateTime start = DateTime.Now;

        await Task.WhenAll(options.InputFiles.Select(input => this.ConvertAsync(converter, input, options.OutputDirectory)));

        Console.WriteLine($"done in {(DateTime.Now - start).TotalSeconds} seconds. bye.");
    }
Ejemplo n.º 9
0
    //-------------------------------------------------------------------------
    /// <inheritdoc/>
    public void ExpandWildcards(WorkerOptions options)
    {
#if NET6_0_OR_GREATER
        ArgumentNullException.ThrowIfNull(options);
#else
        if (options is null)
        {
            throw new ArgumentNullException(nameof(options));
        }
#endif

        var expandedFiles = new List <string>();

        foreach (string inpupt in options.InputFiles)
        {
            if (!inpupt.Contains('*'))
            {
                expandedFiles.Add(inpupt);
                continue;
            }

            this.Expand(inpupt, expandedFiles);
        }

        options.InputFiles = expandedFiles;
    }
Ejemplo n.º 10
0
        public async Task ServiceBusQueueMessagePumpUsingManagedIdentity_PublishServiceBusMessage_MessageSuccessfullyProcessed()
        {
            // Arrange
            var    config           = TestConfig.Create();
            string connectionString = config.GetServiceBusConnectionString(ServiceBusEntityType.Queue);
            ServiceBusConnectionStringProperties properties = ServiceBusConnectionStringProperties.Parse(connectionString);

            ServicePrincipal servicePrincipal = config.GetServiceBusServicePrincipal();
            string           tenantId         = config.GetTenantId();

            using (TemporaryEnvironmentVariable.Create(EnvironmentVariables.AzureTenantId, tenantId))
                using (TemporaryEnvironmentVariable.Create(EnvironmentVariables.AzureServicePrincipalClientId, servicePrincipal.ClientId))
                    using (TemporaryEnvironmentVariable.Create(EnvironmentVariables.AzureServicePrincipalClientSecret, servicePrincipal.ClientSecret))
                    {
                        var options = new WorkerOptions();
                        options.AddEventGridPublisher(config)
                        .AddServiceBusQueueMessagePumpUsingManagedIdentity(
                            queueName: properties.EntityPath,
                            serviceBusNamespace: properties.FullyQualifiedNamespace,
                            clientId: servicePrincipal.ClientId,
                            configureMessagePump: opt => opt.AutoComplete = true)
                        .WithServiceBusMessageHandler <OrdersAzureServiceBusMessageHandler, Order>();

                        // Act
                        await using (var worker = await Worker.StartNewAsync(options))
                            await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger))
                            {
                                // Assert
                                await service.SimulateMessageProcessingAsync(connectionString);
                            }
                    }
        }
Ejemplo n.º 11
0
        public async Task ServiceBusTopicMessagePumpWithNamespaceScopedConnectionString_PublishesServiceBusMessage_MessageSuccessfullyProcessed()
        {
            // Arrange
            var    config = TestConfig.Create();
            string topicConnectionString     = config.GetServiceBusConnectionString(ServiceBusEntityType.Topic);
            var    properties                = ServiceBusConnectionStringProperties.Parse(topicConnectionString);
            string namespaceConnectionString = properties.GetNamespaceConnectionString();

            var options = new WorkerOptions();

            options.AddEventGridPublisher(config)
            .AddServiceBusTopicMessagePump(
                topicName: properties.EntityPath,
                subscriptionName: "Test-Receive-All-Topic-Only",
                getConnectionStringFromConfigurationFunc: configuration => namespaceConnectionString,
                configureMessagePump: opt => opt.AutoComplete = true)
            .WithServiceBusMessageHandler <OrdersAzureServiceBusMessageHandler, Order>();

            // Act
            await using (var worker = await Worker.StartNewAsync(options))
                await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger))
                {
                    // Assert
                    await service.SimulateMessageProcessingAsync(topicConnectionString);
                }
        }
Ejemplo n.º 12
0
        public async Task ServiceBusQueueMessagePumpWithContextAndBodyFilteringWithSerializer_RoutesServiceBusMessage_MessageSuccessfullyProcessed()
        {
            // Arrange
            var    config           = TestConfig.Create();
            string connectionString = config.GetServiceBusConnectionString(ServiceBusEntityType.Queue);
            var    options          = new WorkerOptions();

            options.AddEventGridPublisher(config)
            .AddServiceBusQueueMessagePump(configuration => connectionString, opt => opt.AutoComplete = true)
            .WithServiceBusMessageHandler <PassThruOrderMessageHandler, Order>(messageContextFilter: context => false)
            .WithServiceBusMessageHandler <CustomerMessageHandler, Customer>(messageBodyFilter: message => true)
            .WithServiceBusMessageHandler <OrderBatchMessageHandler, OrderBatch>(
                messageContextFilter: context => context != null,
                messageBodySerializerImplementationFactory: serviceProvider =>
            {
                var logger = serviceProvider.GetService <ILogger <OrderBatchMessageBodySerializer> >();
                return(new OrderBatchMessageBodySerializer(logger));
            },
                messageBodyFilter: message => message.Orders.Length == 1);

            // Act
            await using (var worker = await Worker.StartNewAsync(options))
                await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger))
                {
                    // Assert
                    await service.SimulateMessageProcessingAsync(connectionString);
                }
        }
Ejemplo n.º 13
0
        public async Task ServiceBusQueueMessagePumpWithServiceBusDeadLetterOnFallback_PublishServiceBusMessage_MessageSuccessfullyProcessed()
        {
            // Arrange
            var    config           = TestConfig.Create();
            string connectionString = config.GetServiceBusConnectionString(ServiceBusEntityType.Queue);
            var    options          = new WorkerOptions();

            options.AddServiceBusQueueMessagePump(
                configuration => connectionString,
                opt => opt.AutoComplete = false)
            .WithServiceBusMessageHandler <ShipmentAzureServiceBusMessageHandler, Shipment>((AzureServiceBusMessageContext context) => true)
            .WithServiceBusFallbackMessageHandler <OrdersAzureServiceBusDeadLetterFallbackMessageHandler>();

            Order order = OrderGenerator.Generate();

            await using (var worker = await Worker.StartNewAsync(options))
                await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger))
                {
                    // Act
                    await service.SendMessageToServiceBusAsync(connectionString, order.AsServiceBusMessage());

                    // Assert
                    await service.AssertDeadLetterMessageAsync(connectionString);
                }
        }
        public async Task ConnectWorker_WatchesOnlySpecifiedTubes()
        {
            await ConnectAsync();

            await prod.UseAsync("watched");

            await DrainUsedTube();

            int counter = 0;
            var options = new WorkerOptions {
                Tubes = { "watched" }
            };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async(c, job) =>
            {
                counter++;
                await c.DeleteAsync();
            });

            using (await worker)
            {
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

                await prod.UseAsync("default");

                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

                await Task.Delay(200);
            }

            Assert.Equal(1, counter);
        }
Ejemplo n.º 15
0
 public Worker(ILogger <Worker> logger, WorkerOptions options, ICelebration birthdayService, ICommunication comms)
 {
     _logger          = logger;
     _options         = options;
     _birthdayService = birthdayService;
     this.comms       = comms;
 }
Ejemplo n.º 16
0
        public void Output_arg_given_but_no_value___throws_ArgumentException()
        {
            string[] args = { "a.trx", "--output" };

            ArgumentException actual = Assert.Throws <ArgumentException>(() => WorkerOptions.Parse(args));

            Assert.AreEqual("--output specified, but no value is given. An output-directory needs to specified in this case.", actual.Message);
        }
 private static void AddServiceBusMessagePump(WorkerOptions options, KeyRotationConfig rotationConfig, string jobId)
 {
     options.AddServiceBusQueueMessagePump(rotationConfig.KeyVault.SecretName, opt =>
     {
         opt.JobId = jobId;
         // Unrealistic big maximum exception count so that we're certain that the message pump gets restarted based on the notification and not the unauthorized exception.
         opt.MaximumUnauthorizedExceptionsBeforeRestart = 1000;
     }).WithServiceBusMessageHandler <OrdersAzureServiceBusMessageHandler, Order>();
 }
Ejemplo n.º 18
0
    public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) => {
        IConfiguration configuration = hostContext.Configuration;
        WorkerOptions options        = configuration.GetSection("WCF").Get <WorkerOptions>();

        services.AddSingleton(options);
        services.AddHostedService <Worker>();
    });
 private static void AddSecretStore(WorkerOptions options, KeyRotationConfig rotationConfig)
 {
     options.Configure(host => host.ConfigureSecretStore((configuration, stores) =>
     {
         stores.AddAzureKeyVaultWithServicePrincipal(
             rotationConfig.KeyVault.VaultUri,
             rotationConfig.ServicePrincipal.ClientId,
             rotationConfig.ServicePrincipal.ClientSecret)
         .AddConfiguration(configuration);
     }));
 }
Ejemplo n.º 20
0
        public void WorkerOptions_without_wildcards___nothing_changed()
        {
            string[] inputFiles = { "a.txt", "b.txt" };
            var      options    = new WorkerOptions(inputFiles);

            GlobHandler sut = this.CreateSut();

            sut.ExpandWildcards(options);

            CollectionAssert.AreEqual(inputFiles, options.InputFiles);
        }
Ejemplo n.º 21
0
        public Worker(ILogger <Worker> logger, IConfiguration configuration)
        {
            var workerOptions = new WorkerOptions();

            configuration.Bind("Kafka", workerOptions);

            _logger       = logger;
            _kafkaOptions = new KafkaOptions(new Uri(workerOptions.BootstrapServers));
            _brokerRouter = new BrokerRouter(_kafkaOptions);
            _consumer     = new KafkaNet.Consumer(new ConsumerOptions(workerOptions.Topic, _brokerRouter));
        }
Ejemplo n.º 22
0
        public void Convert_to_trx___option_false()
        {
            string[] args = { "a.trx", "--junit2trx" };

            var actual = WorkerOptions.Parse(args);

            Assert.Multiple(() =>
            {
                Assert.IsFalse(actual.ConvertToJunit);
                CollectionAssert.AreEqual(new string[] { "a.trx" }, actual.InputFiles);
            });
        }
 private static void AddEventGridPublisher(WorkerOptions options, TestConfig config)
 {
     options.Services.AddTransient(svc =>
     {
         string eventGridTopic = config.GetTestInfraEventGridTopicUri();
         string eventGridKey   = config.GetTestInfraEventGridAuthKey();
         return(EventGridPublisherBuilder
                .ForTopic(eventGridTopic)
                .UsingAuthenticationKey(eventGridKey)
                .Build());
     });
 }
Ejemplo n.º 24
0
        public JsonPocoConverterTests()
        {
            var options = new WorkerOptions();

            options.Serializer = new JsonObjectSerializer(new JsonSerializerOptions()
            {
                PropertyNameCaseInsensitive = true
            });
            var wrapper = new OptionsWrapper <WorkerOptions>(options);

            _jsonPocoConverter = new JsonPocoConverter(wrapper);
        }
Ejemplo n.º 25
0
    public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) => {
        IConfiguration configuration = new ConfigurationBuilder()
                                       .SetBasePath(Directory.GetCurrentDirectory())
                                       .AddJsonFile("appsettings.json")
                                       .Build();
        WorkerOptions options = configuration.GetSection("WCF").Get <WorkerOptions>();

        services.AddSingleton(options);
        services.AddHostedService <Worker>();
    });
        public async Task ConnectWorker_FollowsFailureOptionsIfTheDeserializerThrows(WorkerFailureBehavior behavior)
        {
            await ConnectAsync();

            await prod.UseAsync("watched");

            await DrainUsedTube();

            // Ensure all messages will fail horribly during deserialization
            var configuration = ConnectionConfiguration.Parse(connectionString);

            configuration.JobSerializer = new ThrowingSerializer();

            bool gotInsideWorkerFunc = false;

            var options = new WorkerOptions
            {
                Tubes               = { "watched" },
                FailureBehavior     = behavior,
                FailureReleaseDelay = TenSeconds,
            };
            var worker = BeanstalkConnection.ConnectWorkerAsync <Jobject>(configuration, options, async(c, _) =>
            {
                gotInsideWorkerFunc = true;
                await c.DeleteAsync();
            });

            int jobId;

            using (await worker)
            {
                jobId = await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);

                await Task.Delay(200);

                Assert.False(gotInsideWorkerFunc);

                var stats = await prod.JobStatisticsAsync(jobId);

                switch (behavior)
                {
                case WorkerFailureBehavior.Delete: Assert.Null(stats); return;

                case WorkerFailureBehavior.Bury: Assert.Equal(JobState.Buried, stats.State); return;

                case WorkerFailureBehavior.Release: Assert.Equal(JobState.Delayed, stats.State); return;

                case WorkerFailureBehavior.NoAction: Assert.Equal(JobState.Reserved, stats.State); return;

                default: throw new Exception("Untested behavior: " + behavior);
                }
            }
        }
Ejemplo n.º 27
0
        public async Task Multiple_files_given___converted()
        {
            string[] expectedFiles = { "nunit.xml", "mstest.xml", "mstest-warning.xml" };
            DeleteExpectedFiles(expectedFiles);

            Worker sut = this.CreateSut();

            string[] args    = { "nunit.trx", "mstest.trx", "mstest-warning.trx" };
            var      options = new WorkerOptions(args);
            await sut.RunAsync(options);

            CheckExpectedFilesExist(expectedFiles);
        }
Ejemplo n.º 28
0
        public void Multiple_files_and_output_path_at_start___OK()
        {
            string[] args = { "--output", "out", "a.trx", "b.trx" };

            var actual = WorkerOptions.Parse(args);

            Assert.Multiple(() =>
            {
                string[] expected = { "a.trx", "b.trx" };
                CollectionAssert.AreEqual(expected, actual.InputFiles);
                Assert.AreEqual("out", actual.OutputDirectory);
            });
        }
Ejemplo n.º 29
0
        public void Single_file_given___OK()
        {
            string[] args = { "a.trx" };

            var actual = WorkerOptions.Parse(args);

            Assert.Multiple(() =>
            {
                string[] expected = { "a.trx" };
                CollectionAssert.AreEqual(expected, actual.InputFiles);
                Assert.IsNull(actual.OutputDirectory);
            });
        }
Ejemplo n.º 30
0
        public void Multiple_output_directories___last_one_used()
        {
            string[] args = { "a.trx", "--output", "out", "b.trx", "--output", "junit-out" };

            var actual = WorkerOptions.Parse(args);

            Assert.Multiple(() =>
            {
                string[] expected = { "a.trx", "b.trx" };
                CollectionAssert.AreEqual(expected, actual.InputFiles);
                Assert.AreEqual("junit-out", actual.OutputDirectory);
            });
        }
        public async Task ConnectWorker_StopsWhenDisposed()
        {
            await ConnectAsync();
            var tube = "i-am-a-tube";

            int counter = 0;
            var options = new WorkerOptions { Tubes = { tube } };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async (c, job) =>
            {
                counter++;
                await c.DeleteAsync();
            });

            using (await worker)
            {
                await prod.UseAsync(tube);
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await Task.Delay(200);
            }

            Assert.True(counter > 0);
            var finalValue = counter;

            await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
            await Task.Delay(200);

            Assert.Equal(finalValue, counter);
        }
        public async Task ConnectWorker_WatchesOnlySpecifiedTubes()
        {
            await ConnectAsync();
            await prod.UseAsync("watched");
            await DrainUsedTube();

            int counter = 0;
            var options = new WorkerOptions { Tubes = { "watched" } };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async (c, job) =>
            {
                counter++;
                await c.DeleteAsync();
            });

            using (await worker)
            {
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await prod.UseAsync("default");
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await Task.Delay(200);
            }

            Assert.Equal(1, counter);
        }
        public async Task ConnectWorker_CallsTheWorkerOnTheCurrentSyncContextByDefault()
        {
            await ConnectAsync();

            int counter = 0;
            int wrongContextCount = 0;
            SynchronizationContext startingContext = SynchronizationContext.Current;
            var options = new WorkerOptions { };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async (c, job) =>
            {
                counter++;
                if (startingContext != SynchronizationContext.Current) wrongContextCount++;
                await c.DeleteAsync();
            });

            using (await worker)
            {
                foreach (var _ in Enumerable.Repeat(0, 10))
                    await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await Task.Delay(100);
            }

            Assert.NotEqual(0, counter);
            Assert.Equal(0, wrongContextCount);
        }
        public async Task ConnectWorker_ThrownExceptionFollowsSpecifiedBehavior(WorkerFailureBehavior behavior)
        {
            await ConnectAsync();
            var tube = "test-failure-behaviors";
            await prod.UseAsync(tube);
            await DrainUsedTube();

            var options = new WorkerOptions
            {
                Tubes = { tube },
                FailureBehavior = behavior,
                FailurePriority = 1,
                FailureReleaseDelay = TimeSpan.FromSeconds(10),
            };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, (c, job) =>
            {
                throw new Exception();
            });

            JobStatistics stats;
            using (await worker)
            {
                var id = await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await Task.Delay(100);
                stats = await prod.JobStatisticsAsync(id);
            }

            switch (behavior)
            {
                case WorkerFailureBehavior.Delete: Assert.Null(stats); return;
                case WorkerFailureBehavior.Bury: Assert.Equal(JobState.Buried, stats.State); return;
                case WorkerFailureBehavior.Release: Assert.Equal(JobState.Delayed, stats.State); return;
                case WorkerFailureBehavior.NoAction: Assert.Equal(JobState.Reserved, stats.State); return;
                default: throw new Exception("Untested behavior");
            }
        }
        public async Task ConnectWorker_FollowsFailureOptionsIfTheWorkerFuncDoesntTakeAction()
        {
            await ConnectAsync();
            await prod.UseAsync("watched");
            await DrainUsedTube();

            int receivedJobId = 0;
            var options = new WorkerOptions { Tubes = { "watched" }, FailureBehavior = WorkerFailureBehavior.Bury };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async (c, job) =>
            {
                if (receivedJobId == 0) receivedJobId = job.Id;
                await Task.Delay(0); // Don't call any IWorker methods
            });

            using (await worker)
            {
                await prod.PutAsync(new byte[] { }, 1, TenSeconds, ZeroSeconds);
                await Task.Delay(100);
            }

            var stat = await prod.JobStatisticsAsync(receivedJobId);
            Assert.Equal(JobState.Buried, stat.State);
        }
        public async Task ConnectWorker_CreatesTheSpecifiedNumberOfWorkers()
        {
            var tube = "5-second-tasks";

            await ConnectAsync();
            await prod.UseAsync(tube);
            await Task.WhenAll(
                prod.PutAsync(new byte[] { }, 1, TenSeconds),
                prod.PutAsync(new byte[] { }, 1, TenSeconds),
                prod.PutAsync(new byte[] { }, 1, TenSeconds));

            var reserved = new List<Job>();
            var options = new WorkerOptions { Tubes = { tube }, NumberOfWorkers = 3 };
            var worker = BeanstalkConnection.ConnectWorkerAsync(connectionString, options, async (c, job) =>
            {
                reserved.Add(job);
                await Task.Delay(5000);
                await c.DeleteAsync();
            });

            var stats = new List<JobStatistics>();
            using (await worker)
            {
                await Task.Delay(200);
                foreach (var job in reserved)
                    stats.Add(await prod.JobStatisticsAsync(job.Id));
            }

            Assert.Equal(3, stats.Count);
            Assert.All(stats, stat => Assert.Equal(JobState.Reserved, stat.State));
        }