Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace NServiceBus.AcceptanceTests.Core.Installers;

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

static class ServiceProviderExtensions
{
extension(IServiceProvider provider)
{
public async Task RunHostedServices(CancellationToken cancellationToken = default)
{
// We don't have host support in the acceptance tests, so we need to manually start/stop the services
var hostedServices = provider.GetServices<IHostedService>().ToList();
foreach (var hostedService in hostedServices)
{
await hostedService.StartAsync(cancellationToken);
}

hostedServices.Reverse();
foreach (var hostedService in hostedServices)
{
await hostedService.StopAsync(cancellationToken);

if (hostedService is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
namespace NServiceBus.AcceptanceTests.Core.Installers;

using System;
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using Configuration.AdvancedExtensibility;
using FakeTransport;
using Features;
using Installation;
Expand All @@ -15,79 +16,74 @@ public class When_installing_endpoint : NServiceBusAcceptanceTest
[Test]
public async Task Should_only_execute_setup_and_complete()
{
var context = await Scenario.Define<Context>()
.WithComponent(new InstallationOnlyComponent<EndpointWithInstaller>())
var fakeTransport = new FakeTransport();
var endpointConfiguration = new EndpointConfiguration("EndpointWithInstaller");
endpointConfiguration.AssemblyScanner().Disable = true;
endpointConfiguration.UsePersistence<AcceptanceTestingPersistence>();
endpointConfiguration.UseTransport(fakeTransport);
endpointConfiguration.UseSerialization<SystemJsonSerializer>();

endpointConfiguration.EnableFeature<CustomFeature>();

var context = await Scenario.Define<Context>(ctx =>
{
endpointConfiguration.GetSettings().Set(ctx);
})
.WithServices(services => services.AddNServiceBusEndpointInstaller(endpointConfiguration))
.WithServiceResolve(static async (provider, token) => await provider.RunHostedServices(token))
.Run();

using (Assert.EnterMultipleScope())
{
Assert.That(context.InstallerCalled, Is.True, "Should run installers");
Assert.That(context.FeatureSetupCalled, Is.True, "Should initialize Features");
Assert.That(context.FeatureStartupTaskCalled, Is.False, "Should not start FeatureStartupTasks");
}

Assert.That(new[]
{
$"{nameof(TransportDefinition)}.{nameof(TransportDefinition.Initialize)}", $"{nameof(IMessageReceiver)}.{nameof(IMessageReceiver.Initialize)} for receiver Main",
}, Is.EqualTo(context.TransportStartupSequence).AsCollection, "Should not start the receivers");
}, Is.EqualTo(fakeTransport.StartupSequence).AsCollection, "Should not start the receivers");
}

class Context : ScenarioContext
{
public bool InstallerCalled { get; set; }
public bool FeatureSetupCalled { get; set; }
public bool FeatureStartupTaskCalled { get; set; }
public FakeTransport.StartUpSequence TransportStartupSequence { get; set; }

public void MaybeCompleted() => MarkAsCompleted(InstallerCalled, FeatureSetupCalled, TransportStartupSequence != null);
public void MaybeCompleted() => MarkAsCompleted(InstallerCalled, FeatureSetupCalled);
}

class EndpointWithInstaller : EndpointConfigurationBuilder
class CustomInstaller(Context testContext) : INeedToInstallSomething
{
public EndpointWithInstaller() =>
EndpointSetup<DefaultServer>((c, r) =>
{
c.EnableFeature<CustomFeature>();

// Register FakeTransport to track transport seam usage during installation
var fakeTransport = new FakeTransport();
c.UseTransport(fakeTransport);
((Context)r.ScenarioContext).TransportStartupSequence = fakeTransport.StartupSequence;
});

class CustomInstaller(Context testContext) : INeedToInstallSomething
public Task Install(string identity, CancellationToken cancellationToken = default)
{
public Task Install(string identity, CancellationToken cancellationToken = default)
{
testContext.InstallerCalled = true;
testContext.MaybeCompleted();
return Task.CompletedTask;
}
testContext.InstallerCalled = true;
testContext.MaybeCompleted();
return Task.CompletedTask;
}
}

class CustomFeature : Feature
class CustomFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
protected override void Setup(FeatureConfigurationContext context)
{
context.AddInstaller<CustomInstaller>();
context.AddInstaller<CustomInstaller>();

var testContext = context.Settings.Get<Context>();
testContext.FeatureSetupCalled = true;
var testContext = context.Settings.Get<Context>();
testContext.FeatureSetupCalled = true;

context.RegisterStartupTask(new CustomFeatureStartupTask(testContext));
}
context.RegisterStartupTask(new CustomFeatureStartupTask(testContext));
}

class CustomFeatureStartupTask(Context testContext) : FeatureStartupTask
class CustomFeatureStartupTask(Context testContext) : FeatureStartupTask
{
protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
{
protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
{
testContext.FeatureStartupTaskCalled = true;
testContext.MaybeCompleted();
return Task.CompletedTask;
}

protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.CompletedTask;
testContext.MarkAsFailed(new InvalidOperationException("FeatureStartupTask should not be called"));
return Task.CompletedTask;
}

protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
namespace NServiceBus.AcceptanceTests.Core.Installers;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
using Configuration.AdvancedExtensibility;
using FakeTransport;
using Features;
using Installation;
using NUnit.Framework;
using Settings;
using Transport;

public class When_installing_endpoints : NServiceBusAcceptanceTest
{
[Test]
public async Task Should_only_execute_setup_and_complete()
{
var transport1 = new FakeTransport();
var endpointConfiguration1 = CreateEndpointConfiguration(transport1, "EndpointWithInstaller1");

var transport2 = new FakeTransport();
EndpointConfiguration endpointConfiguration2 = CreateEndpointConfiguration(transport2, "EndpointWithInstaller2");

var context = await Scenario.Define<Context>(ctx =>
{
endpointConfiguration1.GetSettings().Set(ctx);
endpointConfiguration2.GetSettings().Set(ctx);
})
.WithServices(services =>
{
services.AddNServiceBusEndpointInstaller(endpointConfiguration1, "EndpointWithInstaller1");
services.AddNServiceBusEndpointInstaller(endpointConfiguration2, "EndpointWithInstaller2");
})
.WithServiceResolve(static async (provider, token) => await provider.RunHostedServices(token))
.Run();

using (Assert.EnterMultipleScope())
{
Assert.That(context.InstallerCalled.All(x => x.Value), Is.True, "Should run installers");
Assert.That(context.FeatureSetupCalled.All(x => x.Value), Is.True, "Should initialize Features");
}

using (Assert.EnterMultipleScope())
{
Assert.That(new[]
{
$"{nameof(TransportDefinition)}.{nameof(TransportDefinition.Initialize)}", $"{nameof(IMessageReceiver)}.{nameof(IMessageReceiver.Initialize)} for receiver Main",
}, Is.EqualTo(transport1.StartupSequence).AsCollection, "Should not start the receivers");
Assert.That(new[]
{
$"{nameof(TransportDefinition)}.{nameof(TransportDefinition.Initialize)}", $"{nameof(IMessageReceiver)}.{nameof(IMessageReceiver.Initialize)} for receiver Main",
}, Is.EqualTo(transport2.StartupSequence).AsCollection, "Should not start the receivers");
}
}

static EndpointConfiguration CreateEndpointConfiguration(FakeTransport transport, string endpointName)
{
var endpointConfiguration = new EndpointConfiguration(endpointName);
endpointConfiguration.AssemblyScanner().Disable = true;
endpointConfiguration.UsePersistence<AcceptanceTestingPersistence>();
endpointConfiguration.UseTransport(transport);
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
endpointConfiguration.EnableFeature<CustomFeature>();
return endpointConfiguration;
}

class Context : ScenarioContext
{
public Dictionary<string, bool> InstallerCalled { get; set; } = [];
public Dictionary<string, bool> FeatureSetupCalled { get; set; } = [];

public void MaybeCompleted() => MarkAsCompleted(InstallerCalled.Count == 2, FeatureSetupCalled.Count == 2);
}

class CustomInstaller(Context testContext, IReadOnlySettings settings) : INeedToInstallSomething
{
public Task Install(string identity, CancellationToken cancellationToken = default)
{
testContext.InstallerCalled[settings.EndpointName()] = true;
testContext.MaybeCompleted();
return Task.CompletedTask;
}
}

class CustomFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
context.AddInstaller<CustomInstaller>();

var settings = context.Settings;

var testContext = settings.Get<Context>();
testContext.FeatureSetupCalled[settings.EndpointName()] = true;

context.RegisterStartupTask(new CustomFeatureStartupTask(testContext));
}

class CustomFeatureStartupTask(Context testContext) : FeatureStartupTask
{
protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
{
testContext.MarkAsFailed(new InvalidOperationException("FeatureStartupTask should not be called"));
return Task.CompletedTask;
}

protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,7 @@ namespace NServiceBus
public static class ServiceCollectionExtensions
{
public static void AddNServiceBusEndpoint(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, NServiceBus.EndpointConfiguration endpointConfiguration, object? endpointIdentifier = null) { }
public static void AddNServiceBusEndpointInstaller(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, NServiceBus.EndpointConfiguration endpointConfiguration, object? endpointIdentifier = null) { }
}
public static class SettingsExtensions
{
Expand Down Expand Up @@ -1573,9 +1574,12 @@ namespace NServiceBus.Installation
{
System.Threading.Tasks.Task Install(string identity, System.Threading.CancellationToken cancellationToken = default);
}
[System.Obsolete(@"Self-hosting installers is no longer recommended. Instead, consider using a Microsoft IHostApplicationBuilder-based host to manage the installation lifecycle of your endpoint. Use 'IServiceCollection.AddNServiceBusEndpointInstaller' instead. Will be treated as an error from version 11.0.0. Will be removed in version 12.0.0.", false)]
public static class Installer
{
[System.Obsolete(@"Self-hosting installers is no longer recommended. Instead, consider using a Microsoft IHostApplicationBuilder-based host to manage the installation lifecycle of your endpoint. Use 'IServiceCollection.AddNServiceBusEndpointInstaller' instead. Will be treated as an error from version 11.0.0. Will be removed in version 12.0.0.", false)]
public static NServiceBus.Installation.InstallerWithExternallyManagedContainer CreateInstallerWithExternallyManagedContainer(NServiceBus.EndpointConfiguration configuration, Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection) { }
[System.Obsolete(@"Self-hosting installers is no longer recommended. Instead, consider using a Microsoft IHostApplicationBuilder-based host to manage the installation lifecycle of your endpoint. Use 'IServiceCollection.AddNServiceBusEndpointInstaller' instead. Will be treated as an error from version 11.0.0. Will be removed in version 12.0.0.", false)]
public static System.Threading.Tasks.Task Setup(NServiceBus.EndpointConfiguration configuration, System.Threading.CancellationToken cancellationToken = default) { }
}
public class InstallerWithExternallyManagedContainer
Expand Down
Loading