diff --git a/.gitattributes b/.gitattributes index bca64ed74..0b6aa0246 100644 --- a/.gitattributes +++ b/.gitattributes @@ -22,6 +22,8 @@ *.pptx -text -diff *.xap -text -diff *.ico -text -diff +*.ttf -text -diff +*.otf -text -diff *.cs text diff=csharp *.config text diff=csharp @@ -53,9 +55,11 @@ *.fs text *.fsx text *.hs text - +*.targets text *.psm1 text *.ps1 text +*.md text +*.DotSettings text *.txt text eol=crlf *.bat text eol=crlf diff --git a/.gitignore b/.gitignore index 81e89fb6c..ef7a9f241 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,13 @@ nugets +deploy build32 binaries -obj -bin *.vshost.* .nu -_ReSharper.* _UpgradeReport.* -*.csproj.user -*.resharper.user -*.resharper -*.suo *.cache *~ *.swp -*.user -TestResults -TestResult.xml results CommonAssemblyInfo.cs lib/sqlite/System.Data.SQLite.dll @@ -35,4 +26,60 @@ _NCrunch_NServiceBus/* logs run-git.cmd src/Chocolatey/Build/* -App_Packages + +installer/[F|f]iles +installer/[C|c]ustom[A|a]ctions +installer/ServiceControl-cache + +# Created by https://www.gitignore.io + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +.vs/ + +# mac temp file ignore +.DS_Store + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +src/scaffolding.config + +# Approval tests temp file +*.received.* \ No newline at end of file diff --git a/buildsupport/FubuCore.dll b/buildsupport/FubuCore.dll deleted file mode 100644 index de611bc01..000000000 Binary files a/buildsupport/FubuCore.dll and /dev/null differ diff --git a/buildsupport/GitVersionTask/Build/GitVersionTask.targets b/buildsupport/GitVersionTask/Build/GitVersionTask.targets deleted file mode 100644 index d88232f59..000000000 --- a/buildsupport/GitVersionTask/Build/GitVersionTask.targets +++ /dev/null @@ -1,48 +0,0 @@ - - - - $(MSBuildProjectDirectory)..\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/buildsupport/GitVersionTask/GitVersion.exe b/buildsupport/GitVersionTask/GitVersion.exe deleted file mode 100644 index 8145ba7ed..000000000 Binary files a/buildsupport/GitVersionTask/GitVersion.exe and /dev/null differ diff --git a/buildsupport/GitVersionTask/GitVersionTask.dll b/buildsupport/GitVersionTask/GitVersionTask.dll deleted file mode 100644 index 035d8acd5..000000000 Binary files a/buildsupport/GitVersionTask/GitVersionTask.dll and /dev/null differ diff --git a/buildsupport/GitVersionTask/LibGit2Sharp.dll b/buildsupport/GitVersionTask/LibGit2Sharp.dll deleted file mode 100644 index a8e940c4b..000000000 Binary files a/buildsupport/GitVersionTask/LibGit2Sharp.dll and /dev/null differ diff --git a/buildsupport/GitVersionTask/NativeBinaries/amd64/git2-65e9dc6.dll b/buildsupport/GitVersionTask/NativeBinaries/amd64/git2-65e9dc6.dll deleted file mode 100644 index d548c861e..000000000 Binary files a/buildsupport/GitVersionTask/NativeBinaries/amd64/git2-65e9dc6.dll and /dev/null differ diff --git a/buildsupport/GitVersionTask/NativeBinaries/x86/git2-65e9dc6.dll b/buildsupport/GitVersionTask/NativeBinaries/x86/git2-65e9dc6.dll deleted file mode 100644 index 040f87c75..000000000 Binary files a/buildsupport/GitVersionTask/NativeBinaries/x86/git2-65e9dc6.dll and /dev/null differ diff --git a/buildsupport/MSBuild.Community.Tasks.dll b/buildsupport/MSBuild.Community.Tasks.dll deleted file mode 100644 index 6d43c0e5b..000000000 Binary files a/buildsupport/MSBuild.Community.Tasks.dll and /dev/null differ diff --git a/buildsupport/Microsoft.Web.XmlTransform.dll b/buildsupport/Microsoft.Web.XmlTransform.dll deleted file mode 100644 index 65769fa50..000000000 Binary files a/buildsupport/Microsoft.Web.XmlTransform.dll and /dev/null differ diff --git a/buildsupport/NuGet.Core.dll b/buildsupport/NuGet.Core.dll deleted file mode 100644 index a5787205f..000000000 Binary files a/buildsupport/NuGet.Core.dll and /dev/null differ diff --git a/buildsupport/PepitaPackage.dll b/buildsupport/PepitaPackage.dll deleted file mode 100644 index d1509d1b2..000000000 Binary files a/buildsupport/PepitaPackage.dll and /dev/null differ diff --git a/buildsupport/RippleRestoreTask.dll b/buildsupport/RippleRestoreTask.dll deleted file mode 100644 index e443473c5..000000000 Binary files a/buildsupport/RippleRestoreTask.dll and /dev/null differ diff --git a/buildsupport/RippleRestoreTask.targets b/buildsupport/RippleRestoreTask.targets deleted file mode 100644 index cb3f1b839..000000000 --- a/buildsupport/RippleRestoreTask.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - $(MSBuildProjectDirectory)\..\ - - RippleRestoreTarget; - $(BuildDependsOn); - - - - - - - - - - - - - - - - - - - diff --git a/buildsupport/ripple.exe b/buildsupport/ripple.exe deleted file mode 100644 index 5ed129f4c..000000000 Binary files a/buildsupport/ripple.exe and /dev/null differ diff --git a/packaging/nuget/NServiceBus.SqlServer.nuspec b/packaging/nuget/NServiceBus.SqlServer.nuspec index 4eefe7a9e..71c8a30f4 100644 --- a/packaging/nuget/NServiceBus.SqlServer.nuspec +++ b/packaging/nuget/NServiceBus.SqlServer.nuspec @@ -3,22 +3,22 @@ NServiceBus.SqlServer NServiceBus SqlServer - 4.0.0-alpha0 - NServiceBus Ltd - Udi Dahan, Andreas Ohlund, John Simons - http://particular.net/LicenseAgreement - http://particular.net/ - http://s3.amazonaws.com/nuget.images/NServiceBus_32.png - true + $version$ + $authors$ + $owners$ + $licenseUrl$ + $projectUrl$ + $iconUrl$ + $requireLicenseAcceptance$ + $copyright$ SQL transport support for NServicebus - Copyright 2010-2013 NServiceBus. All rights reserved nservicebus servicebus msmq cqrs publish subscribe - + \ No newline at end of file diff --git a/ripple.cmd b/ripple.cmd deleted file mode 100644 index 74b6af73f..000000000 --- a/ripple.cmd +++ /dev/null @@ -1 +0,0 @@ -buildsupport\ripple.exe %* \ No newline at end of file diff --git a/ripple.config b/ripple.config deleted file mode 100644 index 443ffb4f8..000000000 --- a/ripple.config +++ /dev/null @@ -1,30 +0,0 @@ - - - NServiceBus.SqlServer - packaging/nuget - src - rake - rake compile - CurrentMajor,NextMajor - Current,NextMajor - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_a_message_is_audited.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_a_message_is_audited.cs new file mode 100644 index 000000000..28ec5898b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_a_message_is_audited.cs @@ -0,0 +1,130 @@ +namespace NServiceBus.AcceptanceTests.Audit +{ + using System; + using System.Linq; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + +#pragma warning disable 612, 618 + + public class When_a_message_is_audited : NServiceBusAcceptanceTest + { + [Test] + public void Should_preserve_the_original_body() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeAudited()))) + .WithEndpoint() + .Done(c => c.AuditChecksum != default(byte)) + .Run(); + + Assert.AreEqual(context.OriginalBodyChecksum, context.AuditChecksum, "The body of the message sent to audit should be the same as the original message coming off the queue"); + } + + [Test] + public void Should_add_the_license_diagnostic_headers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeAudited()))) + .WithEndpoint() + .Done(c => c.HasDiagnosticLicensingHeaders) + .Run(); + Assert.IsTrue(context.HasDiagnosticLicensingHeaders); + } + + + public class Context : ScenarioContext + { + public byte OriginalBodyChecksum { get; set; } + public byte AuditChecksum { get; set; } + public bool HasDiagnosticLicensingHeaders { get; set; } + } + + public class EndpointWithAuditOn : EndpointConfigurationBuilder + { + public EndpointWithAuditOn() + { + EndpointSetup() + .AuditTo(); + } + + class BodyMutator : IMutateIncomingTransportMessages, INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + + var originalBody = transportMessage.Body; + + Context.OriginalBodyChecksum = Checksum(originalBody); + + // modifying the body by adding a line break + var modifiedBody = new byte[originalBody.Length + 1]; + + Buffer.BlockCopy(originalBody, 0, modifiedBody, 0, originalBody.Length); + + modifiedBody[modifiedBody.Length - 1] = 13; + + transportMessage.Body = modifiedBody; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MessageToBeAuditedHandler : IHandleMessages { public void Handle(MessageToBeAudited message) { } } + } + + class AuditSpyEndpoint : EndpointConfigurationBuilder + { + public AuditSpyEndpoint() + { + EndpointSetup(); + } + + class BodySpy : IMutateIncomingTransportMessages, INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.AuditChecksum = Checksum(transportMessage.Body); + string licenseExpired; + Context.HasDiagnosticLicensingHeaders = transportMessage.Headers.TryGetValue(Headers.HasLicenseExpired, out licenseExpired); + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MessageToBeAuditedHandler : IHandleMessages { public void Handle(MessageToBeAudited message) { } } + } + + public static byte Checksum(byte[] data) + { + var longSum = data.Sum(x => (long)x); + return unchecked((byte)longSum); + } + + [Serializable] + public class MessageToBeAudited : IMessage + { + } + + + } + +#pragma warning restore 612, 618 + +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_using_auditing_as_a_feature.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_using_auditing_as_a_feature.cs new file mode 100644 index 000000000..1df939607 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Audit/When_using_auditing_as_a_feature.cs @@ -0,0 +1,111 @@ + +namespace NServiceBus.AcceptanceTests.Audit +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_using_auditing_as_a_feature : NServiceBusAcceptanceTest + { + [Test] + public void Message_should_not_be_forwarded_to_auditQueue_when_auditing_is_disabled() + { + var context = new Context(); + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeAudited()))) + .WithEndpoint() + .Done(c => c.IsMessageHandlingComplete) + .Run(); + + Assert.IsFalse(context.IsMessageHandledByTheAuditEndpoint); + } + + [Test] + public void Message_should_be_forwarded_to_auditQueue_when_auditing_is_enabled() + { + var context = new Context(); + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeAudited()))) + .WithEndpoint() + .Done(c => c.IsMessageHandlingComplete) + .Run(); + + Assert.IsTrue(context.IsMessageHandledByTheAuditEndpoint); + } + + public class Context : ScenarioContext + { + public bool IsMessageHandlingComplete { get; set; } + public bool IsMessageHandledByTheAuditEndpoint { get; set; } + } + + public class EndpointWithAuditOff : EndpointConfigurationBuilder + { + + public EndpointWithAuditOff() + { + // Although the AuditTo seems strange here, this test tries to fake the scenario where + // even though the user has specified audit config, because auditing is explicitly turned + // off, no messages should be audited. + EndpointSetup(c => Configure.Features.Disable()) + .AuditTo(); + + } + + class MessageToBeAuditedHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageToBeAudited message) + { + MyContext.IsMessageHandlingComplete = true; + } + } + } + + public class EndpointWithAuditOn : EndpointConfigurationBuilder + { + + public EndpointWithAuditOn() + { + EndpointSetup() + .AuditTo(); + } + + class MessageToBeAuditedHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageToBeAudited message) + { + MyContext.IsMessageHandlingComplete = true; + } + } + } + + public class EndpointThatHandlesAuditMessages : EndpointConfigurationBuilder + { + + public EndpointThatHandlesAuditMessages() + { + EndpointSetup(); + } + + class AuditMessageHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageToBeAudited message) + { + MyContext.IsMessageHandledByTheAuditEndpoint = true; + } + } + } + + [Serializable] + public class MessageToBeAudited : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_Deferring_a_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_Deferring_a_message.cs new file mode 100644 index 000000000..172e1bf16 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_Deferring_a_message.cs @@ -0,0 +1,52 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_Deferring_a_message : NServiceBusAcceptanceTest + { + [Test] + public void Message_should_be_received() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Defer(TimeSpan.FromSeconds(3), new MyMessage()))) + .Done(c => c.WasCalled) + .Run(); + + Assert.IsTrue(context.WasCalled); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + Context.WasCalled = true; + } + } + } + + [Serializable] + public class MyMessage : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_aborting_the_behavior_chain.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_aborting_the_behavior_chain.cs new file mode 100644 index 000000000..dcb50ea51 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_aborting_the_behavior_chain.cs @@ -0,0 +1,73 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + + public class When_aborting_the_behavior_chain : NServiceBusAcceptanceTest + { + [Test] + public void Subsequent_handlers_will_not_be_invoked() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.FirstHandlerInvoked) + .Run(); + + Assert.That(context.FirstHandlerInvoked, Is.True); + Assert.That(context.SecondHandlerInvoked, Is.False); + } + + public class Context : ScenarioContext + { + public bool FirstHandlerInvoked { get; set; } + public bool SecondHandlerInvoked { get; set; } + } + + [Serializable] + public class SomeMessage : IMessage { } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup(); + } + + class EnsureOrdering : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.Specify(First.Then()); + } + } + + class FirstHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(SomeMessage message) + { + Context.FirstHandlerInvoked = true; + + Bus.DoNotContinueDispatchingCurrentMessageToHandlers(); + } + } + + class SecondHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(SomeMessage message) + { + Context.SecondHandlerInvoked = true; + } + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_handling_current_message_later.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_handling_current_message_later.cs new file mode 100644 index 000000000..fcdf9301e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_handling_current_message_later.cs @@ -0,0 +1,157 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using System.Linq; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + + public class When_handling_current_message_later : NServiceBusAcceptanceTest + { + [Test] + public void Should_commit_unit_of_work() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + Assert.That(context.AnotherMessageReceivedCount, Is.EqualTo(2), + "First handler sends a message to self, which should result in AnotherMessage being dispatched twice"); + } + + [Test, Description("NOTE the double negation - we should probably modify this behavior somehow")] + public void Should_not_not_execute_subsequent_handlers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + Assert.That(context.ThirdHandlerInvocationCount, Is.EqualTo(2), + "Since calling HandleCurrentMessageLater does not discontinue message dispatch, the third handler should be called twice as well"); + } + + [Test] + public void Handlers_are_executed_in_the_right_order() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + var events = context.Events; + CollectionAssert + .AreEquivalent(new[] + { + "FirstHandler:Executed", + "SecondHandler:Sending message to the back of the queue", + "ThirdHandler:Executed", + "FirstHandler:Executed", + "SecondHandler:Handling the message this time", + "ThirdHandler:Executed" + }, + events); + } + + public class Context : ScenarioContext + { + public string[] Events { get; set; } + + public bool SomeMessageHasBeenRequeued { get; set; } + + public bool Done + { + get { return ThirdHandlerInvocationCount >= 2 && AnotherMessageReceivedCount == 2; } + } + + public int AnotherMessageReceivedCount { get; set; } + + public int ThirdHandlerInvocationCount { get; set; } + } + + [Serializable] + public class SomeMessage : IMessage { } + + [Serializable] + public class AnotherMessage : IMessage { } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup(); + } + + class EnsureOrdering : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.Specify(First.Then().AndThen()); + } + } + + class FirstHandler : IHandleMessages, IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + + public void Handle(SomeMessage message) + { + Context.RegisterEvent("FirstHandler:Executed"); + Bus.SendLocal(new AnotherMessage()); + } + + public void Handle(AnotherMessage message) + { + Context.AnotherMessageReceivedCount++; + } + } + + class SecondHandler : IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + public void Handle(SomeMessage message) + { + if (!Context.SomeMessageHasBeenRequeued) + { + Context.RegisterEvent("SecondHandler:Sending message to the back of the queue"); + Bus.HandleCurrentMessageLater(); + Context.SomeMessageHasBeenRequeued = true; + } + else + { + Context.RegisterEvent("SecondHandler:Handling the message this time"); + } + } + } + + class ThirdHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(SomeMessage message) + { + Context.RegisterEvent("ThirdHandler:Executed"); + Context.ThirdHandlerInvocationCount++; + } + } + } + } + + static class ContextEx + { + public static void RegisterEvent(this When_handling_current_message_later.Context context, string description) + { + context.Events = context.Events + .Concat(new[] {description}) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_registering_a_callback_for_a_local_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_registering_a_callback_for_a_local_message.cs new file mode 100644 index 000000000..d00d0102b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_registering_a_callback_for_a_local_message.cs @@ -0,0 +1,64 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_registering_a_callback_for_a_local_message : NServiceBusAcceptanceTest + { + [Test] + public void Should_trigger_the_callback_when_the_response_comes_back() + { + Scenario.Define() + .WithEndpoint(b=>b.Given( + (bus,context)=>bus.SendLocal(new MyRequest()).Register(r => + { + Assert.True(context.HandlerGotTheRequest); + context.CallbackFired = true; + }))) + .Done(c => c.CallbackFired) + .Repeat(r =>r.For(Transports.Default)) + .Should(c => + { + Assert.True(c.CallbackFired); + Assert.True(c.HandlerGotTheRequest); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool HandlerGotTheRequest { get; set; } + + public bool CallbackFired { get; set; } + } + + public class EndpointWithLocalCallback : EndpointConfigurationBuilder + { + public EndpointWithLocalCallback() + { + EndpointSetup(); + } + + public class MyRequestHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest request) + { + Assert.False(Context.CallbackFired); + Context.HandlerGotTheRequest = true; + + Bus.Return(1); + } + } + } + + [Serializable] + public class MyRequest : IMessage{} + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs new file mode 100644 index 000000000..a9c7bc06b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs @@ -0,0 +1,100 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using System.Threading; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint : NServiceBusAcceptanceTest + { + [Test] + public void First_registration_should_be_the_final_destination() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Send(new MyCommand1()))) + .WithEndpoint() + .WithEndpoint() + .Done(c => c.WasCalled1 || c.WasCalled2) + .Run(); + + Assert.IsTrue(context.WasCalled1); + Assert.IsFalse(context.WasCalled2); + } + + public class Context : ScenarioContext + { + public bool WasCalled1 { get; set; } + public bool WasCalled2 { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup() + .AddMapping(typeof(Receiver1)) + .AddMapping(typeof(Receiver2)); + } + } + + public class Receiver1 : EndpointConfigurationBuilder + { + public Receiver1() + { + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyBaseCommand message) + { + Context.WasCalled1 = true; + Thread.Sleep(2000); // Just to be sure the other receiver is finished + } + } + } + + public class Receiver2 : EndpointConfigurationBuilder + { + public Receiver2() + { + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyBaseCommand message) + { + Context.WasCalled2 = true; + Thread.Sleep(2000); // Just to be sure the other receiver is finished + } + } + } + + [Serializable] + public abstract class MyBaseCommand : ICommand + { + } + + [Serializable] + public class MyCommand1 : MyBaseCommand + { + } + + [Serializable] + public class MyCommand2 : MyBaseCommand + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_to_another_endpoint.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_to_another_endpoint.cs new file mode 100644 index 000000000..b4eb71051 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_to_another_endpoint.cs @@ -0,0 +1,99 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using System.Collections.Generic; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_a_message_to_another_endpoint : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_the_message() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.OutgoingHeaders["MyStaticHeader"] = "StaticHeaderValue"; + bus.Send(m=> + { + m.Id = context.Id; + m.SetHeader("MyHeader","MyHeaderValue"); + }); + })) + .WithEndpoint() + .Done(c => c.WasCalled) + .Repeat(r =>r.For(Serializers.Binary) + .For() + ) + .Should(c => + { + Assert.True(c.WasCalled, "The message handler should be called"); + Assert.AreEqual(1, c.TimesCalled, "The message handler should only be invoked once"); + Assert.True(c.ReceivedHeaders[Headers.OriginatingEndpoint].Contains("Sender"), "The sender should attach its endpoint name as a header"); + Assert.True(c.ReceivedHeaders[Headers.ProcessingEndpoint].Contains("Receiver"), "The receiver should attach its endpoint name as a header"); + Assert.AreEqual("StaticHeaderValue",c.ReceivedHeaders["MyStaticHeader"], "Static headers should be attached to outgoing messages"); + Assert.AreEqual("MyHeaderValue", c.MyHeader, "Static headers should be attached to outgoing messages"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + + public int TimesCalled { get; set; } + + public IDictionary ReceivedHeaders { get; set; } + + public Guid Id { get; set; } + + public string MyHeader { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup() + .AddMapping(typeof(Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + EndpointSetup(); + } + } + + [Serializable] + public class MyMessage : ICommand + { + public Guid Id { get; set; } + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + if (Context.Id != message.Id) + return; + + Context.TimesCalled++; + + Context.MyHeader = message.GetHeader("MyHeader"); + + Context.ReceivedHeaders = Bus.CurrentMessageContext.Headers; + + Context.WasCalled = true; + } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_with_no_correlation_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_with_no_correlation_id.cs new file mode 100644 index 000000000..3918a7875 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_sending_a_message_with_no_correlation_id.cs @@ -0,0 +1,73 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class When_sending_a_message_with_no_correlation_id : NServiceBusAcceptanceTest + { + [Test] + public void Should_use_the_message_id_as_the_correlation_id() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new MyRequest()))) + .Done(c => c.GotRequest) + .Run(); + + Assert.AreEqual(context.MessageIdReceived, context.CorrelationIdReceived, "Correlation id should match MessageId"); + } + + public class Context : ScenarioContext + { + public string MessageIdReceived { get; set; } + public bool GotRequest { get; set; } + public string CorrelationIdReceived { get; set; } + } + + public class CorrelationEndpoint : EndpointConfigurationBuilder + { + public CorrelationEndpoint() + { + EndpointSetup(); + } + + class GetValueOfIncomingCorrelationId:IMutateIncomingTransportMessages,INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.CorrelationIdReceived = transportMessage.CorrelationId; + Context.MessageIdReceived = transportMessage.Id; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest response) + { + Context.GotRequest = true; + } + } + } + + + [Serializable] + public class MyRequest : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_starting_a_send_only.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_starting_a_send_only.cs new file mode 100644 index 000000000..f333cb565 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_starting_a_send_only.cs @@ -0,0 +1,22 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using NUnit.Framework; + + public class When_starting_a_send_only : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_need_audit_or_fault_forwarding_config_to_start() + { + using ((IDisposable)Configure.With(new Type[] + { + }) + .DefaultBuilder() + .UnicastBus() + .SendOnly()) + { + } + + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_custom_correlation_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_custom_correlation_id.cs new file mode 100644 index 000000000..f1221c4e1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_custom_correlation_id.cs @@ -0,0 +1,74 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class When_using_a_custom_correlation_id : NServiceBusAcceptanceTest + { + static string CorrelationId = "my_custom_correlation_id"; + + [Test] + public void Should_use_the_given_id_as_the_transport_level_correlation_id() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, CorrelationId, new MyRequest()))) + .Done(c => c.GotRequest) + .Run(); + + Assert.AreEqual(CorrelationId, context.CorrelationIdReceived, "Correlation ids should match"); + } + + public class Context : ScenarioContext + { + public bool GotRequest { get; set; } + + public string CorrelationIdReceived { get; set; } + } + + public class CorrelationEndpoint : EndpointConfigurationBuilder + { + public CorrelationEndpoint() + { + EndpointSetup(); + } + + class GetValueOfIncomingCorrelationId:IMutateIncomingTransportMessages,INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.CorrelationIdReceived = transportMessage.CorrelationId; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest response) + { + Context.GotRequest = true; + } + } + } + + + [Serializable] + public class MyRequest : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_greedy_convention.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_greedy_convention.cs new file mode 100644 index 000000000..c0320ddaf --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_greedy_convention.cs @@ -0,0 +1,64 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_using_a_greedy_convention : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_the_message() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MyMessage + {Id = context.Id}))) + .Done(c => c.WasCalled) + .Repeat(r =>r + .For(Transports.Msmq) + ) + .Should(c => Assert.True(c.WasCalled, "The message handler should be called")) + .Run(); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + + public Guid Id { get; set; } + } + + public class EndPoint : EndpointConfigurationBuilder + { + public EndPoint() + { + EndpointSetup(c => c.DefiningMessagesAs(MessageConvention)); + } + + static bool MessageConvention(Type t) + { + return t.Namespace != null && + (t.Namespace.EndsWith(".Messages") || (t == typeof(MyMessage))); + } + } + + [Serializable] + public class MyMessage + { + public Guid Id { get; set; } + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(MyMessage message) + { + if (Context.Id != message.Id) + return; + + Context.WasCalled = true; + } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_expired.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_expired.cs new file mode 100644 index 000000000..f09474ebb --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_expired.cs @@ -0,0 +1,49 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_using_a_message_with_TimeToBeReceived_has_expired : NServiceBusAcceptanceTest + { + [Test, Ignore("The TTL will only be started at the moment the timeoutmanager sends the message back, still giving the test a second to receive it")] + public void Message_should_not_be_received() + { + var context = new Context(); + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Defer(TimeSpan.FromSeconds(5), new MyMessage()))) + .Run(TimeSpan.FromSeconds(10)); + Assert.IsFalse(context.WasCalled); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + } + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + Context.WasCalled = true; + } + } + } + + [Serializable] + [TimeToBeReceived("00:00:01")] + public class MyMessage : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs new file mode 100644 index 000000000..a516e51da --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs @@ -0,0 +1,53 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_using_a_message_with_TimeToBeReceived_has_not_expired : NServiceBusAcceptanceTest + { + [Test] + public void Message_should_be_received() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new MyMessage()))) + .Done(c => c.WasCalled) + .Run(); + + Assert.IsTrue(context.WasCalled); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + Context.WasCalled = true; + } + } + } + + [Serializable] + [TimeToBeReceived("00:00:10")] + public class MyMessage : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_callbacks_in_a_scaleout_scenario.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_callbacks_in_a_scaleout_scenario.cs new file mode 100644 index 000000000..db1df8120 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BasicMessaging/When_using_callbacks_in_a_scaleout_scenario.cs @@ -0,0 +1,125 @@ +namespace NServiceBus.AcceptanceTests.BasicMessaging +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + using Support; + + public class When_using_callbacks_in_a_scaleout_scenario : NServiceBusAcceptanceTest + { + [Test] + public void Each_client_should_have_a_unique_input_queue_to_avoid_processing_each_others_callbacks() + { + Scenario.Define(() => new Context{Id = Guid.NewGuid()}) + .WithEndpoint(b => b.CustomConfig(c=>RuntimeEnvironment.MachineNameAction = () => "ClientA") + .Given((bus, context) => bus.Send(new MyRequest { Id = context.Id, Client = RuntimeEnvironment.MachineName }) + .Register(r => context.CallbackAFired = true))) + .WithEndpoint(b => b.CustomConfig(c=>RuntimeEnvironment.MachineNameAction = () => "ClientB") + .Given((bus, context) => bus.Send(new MyRequest { Id = context.Id, Client = RuntimeEnvironment.MachineName }) + .Register(r => context.CallbackBFired = true))) + .WithEndpoint() + .Done(c => c.ClientAGotResponse && c.ClientBGotResponse) + .Repeat(r =>r.For() + ) + .Should(c => + { + Assert.True(c.CallbackAFired, "Callback on ClientA should fire"); + Assert.True(c.CallbackBFired, "Callback on ClientB should fire"); + Assert.False(c.ResponseEndedUpAtTheWrongClient, "One of the responses ended up at the wrong client"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + + public bool ClientAGotResponse { get; set; } + + public bool ClientBGotResponse { get; set; } + + public bool ResponseEndedUpAtTheWrongClient { get; set; } + + public bool CallbackAFired { get; set; } + + public bool CallbackBFired { get; set; } + } + + public class Client : EndpointConfigurationBuilder + { + public Client() + { + EndpointSetup(c => Configure.ScaleOut(s => s.UseUniqueBrokerQueuePerMachine())) + .AddMapping(typeof(Server)); + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyResponse response) + { + if (Context.Id != response.Id) + return; + + if (RuntimeEnvironment.MachineName == "ClientA") + Context.ClientAGotResponse = true; + else + { + Context.ClientBGotResponse = true; + } + + if (RuntimeEnvironment.MachineName != response.Client) + Context.ResponseEndedUpAtTheWrongClient = true; + } + } + } + + public class Server : EndpointConfigurationBuilder + { + public Server() + { + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest request) + { + if (Context.Id != request.Id) + return; + + + Bus.Reply(new MyResponse { Id = request.Id,Client = request.Client }); + } + } + } + + [Serializable] + public class MyRequest : IMessage + { + public Guid Id { get; set; } + + public string Client { get; set; } + } + + [Serializable] + public class MyResponse : IMessage + { + public Guid Id { get; set; } + + public string Client { get; set; } + } + + + + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_and_stops_with_a_pending_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_and_stops_with_a_pending_message.cs new file mode 100644 index 000000000..8b252ea7e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_and_stops_with_a_pending_message.cs @@ -0,0 +1,58 @@ +namespace NServiceBus.AcceptanceTests.BusStartStop +{ + using System; + using System.Threading; + using AcceptanceTesting.Support; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_bus_start_and_stops_with_a_pending_message : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_throw() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => SendMessages(b)) + .Run(TimeSpan.FromMilliseconds(100)); + + Thread.Sleep(100); + } + + + EndpointBehaviorBuilder SendMessages(EndpointBehaviorBuilder b) + { + return b.Given((bus, context) => bus.SendLocal(new Message())); + } + + public class Context : ScenarioContext + { + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + + class MessageHandler : IHandleMessages + { + public void Handle(Message message) + { + Thread.Sleep(100); + } + } + } + + [Serializable] + public class Message : IMessage + { + } + + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_raises_an_inmemory_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_raises_an_inmemory_message.cs new file mode 100644 index 000000000..4b2c17a4e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/BusStartStop/When_bus_start_raises_an_inmemory_message.cs @@ -0,0 +1,69 @@ +namespace NServiceBus.AcceptanceTests.BusStartStop +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + +#pragma warning disable 612, 618 + public class When_bus_start_raises_an_inmemory_message : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_throw() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint() + .Done(c => c.GotMessage) + .Repeat(r => r.For()) + .Run(); + } + + public class Context : ScenarioContext + { + public bool GotMessage; + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + + class MessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(Message message) + { + Context.GotMessage = true; + } + } + } + + public class Foo : IWantToRunWhenBusStartsAndStops + { + public IBus Bus { get; set; } + + public void Start() + { + Bus.InMemory.Raise(new Message()); + } + + public void Stop() + { + } + } + + [Serializable] + public class Message : IMessage + { + } + + } +#pragma warning restore 612, 618 + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Configuration/When_a_config_override_is_found.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Configuration/When_a_config_override_is_found.cs new file mode 100644 index 000000000..3e130676c --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Configuration/When_a_config_override_is_found.cs @@ -0,0 +1,65 @@ +namespace NServiceBus.AcceptanceTests.Configuration +{ + using Config; + using Config.ConfigurationSource; + using EndpointTemplates; + using AcceptanceTesting; + using Faults.Forwarder; + using NUnit.Framework; + using Unicast; + using Unicast.Transport; + + public class When_a_config_override_is_found : NServiceBusAcceptanceTest + { + static Address CustomErrorQ = Address.Parse("MyErrorQ"); + + [Test] + public void Should_be_used_instead_of_pulling_the_settings_from_appconfig() + { + var context = Scenario.Define() + .WithEndpoint(b => b.When(c => c.EndpointsStarted, (bus, cc) => + { + var unicastBus = (UnicastBus)bus; + var transport = (TransportReceiver)unicastBus.Transport; + var fm = (FaultManager)transport.FailureManager; + + cc.ErrorQueueUsedByTheEndpoint = fm.ErrorQueue; + cc.IsDone = true; + })) + .Done(c => c.IsDone) + .Run(); + + Assert.AreEqual(CustomErrorQ, context.ErrorQueueUsedByTheEndpoint, "The error queue should have been changed"); + + } + + public class Context : ScenarioContext + { + public bool IsDone { get; set; } + + public Address ErrorQueueUsedByTheEndpoint { get; set; } + } + + public class ConfigOverrideEndpoint : EndpointConfigurationBuilder + { + public ConfigOverrideEndpoint() + { + EndpointSetup(c => c.MessageForwardingInCaseOfFault()); + } + + public class ConfigErrorQueue : IProvideConfiguration + { + public MessageForwardingInCaseOfFaultConfig GetConfiguration() + { + + return new MessageForwardingInCaseOfFaultConfig + { + ErrorQueue = CustomErrorQ.ToString() + }; + } + } + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/DataBus/When_sending_databus_properties.cs b/src/NServiceBus.SqlServer.AcceptanceTests/DataBus/When_sending_databus_properties.cs new file mode 100644 index 000000000..db691ccc6 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/DataBus/When_sending_databus_properties.cs @@ -0,0 +1,67 @@ +namespace NServiceBus.AcceptanceTests.DataBus +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_databus_properties:NServiceBusAcceptanceTest + { + static byte[] PayloadToSend = new byte[1024 * 1024 * 10]; + + [Test] + public void Should_receive_the_message_the_largeproperty_correctly() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus=> bus.Send(new MyMessageWithLargePayload + { + Payload = new DataBusProperty(PayloadToSend) + }))) + .WithEndpoint() + .Done(context => context.ReceivedPayload != null) + .Repeat(r => r.For()) + .Should(c => Assert.AreEqual(PayloadToSend, c.ReceivedPayload, "The large payload should be marshalled correctly using the databus")) + .Run(); + } + + public class Context : ScenarioContext + { + public byte[] ReceivedPayload { get; set; } + } + + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup(c => c.FileShareDataBus(@".\databus\sender")) + .AddMapping(typeof (Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + EndpointSetup(c => c.FileShareDataBus(@".\databus\sender")); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyMessageWithLargePayload messageWithLargePayload) + { + Context.ReceivedPayload = messageWithLargePayload.Payload.Value; + } + } + } + + [Serializable] + public class MyMessageWithLargePayload : ICommand + { + public DataBusProperty Payload { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption.cs new file mode 100644 index 000000000..6c982f323 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption.cs @@ -0,0 +1,117 @@ +namespace NServiceBus.AcceptanceTests.Encryption +{ + using System; + using System.Collections.Generic; + using Config; + using Config.ConfigurationSource; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_using_encryption : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_decrypted_message() + { + Scenario.Define() + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MessageWithSecretData + { + Secret = "betcha can't guess my secret", + SubProperty = new MySecretSubProperty {Secret = "My sub secret"}, + CreditCards = new List + { + new CreditCardDetails + { + ValidTo = DateTime.UtcNow.AddYears(1), + Number = "312312312312312" + }, + new CreditCardDetails + { + ValidTo = DateTime.UtcNow.AddYears(2), + Number = "543645546546456" + } + } + }))) + .Done(c => c.Done) + .Repeat(r => r.For()) + .Should(c => + { + Assert.AreEqual("betcha can't guess my secret", c.Secret); + Assert.AreEqual("My sub secret", c.SubPropertySecret); + CollectionAssert.AreEquivalent(new List { "312312312312312", "543645546546456" }, c.CreditCards); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + + public string Secret { get; set; } + + public string SubPropertySecret { get; set; } + + public List CreditCards { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => c.RijndaelEncryptionService()); + } + + public class Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageWithSecretData message) + { + Context.Secret = message.Secret.Value; + + Context.SubPropertySecret = message.SubProperty.Secret.Value; + + Context.CreditCards = new List + { + message.CreditCards[0].Number.Value, + message.CreditCards[1].Number.Value + }; + + Context.Done = true; + } + } + } + + [Serializable] + public class MessageWithSecretData : IMessage + { + public WireEncryptedString Secret { get; set; } + public MySecretSubProperty SubProperty { get; set; } + public List CreditCards { get; set; } + } + + [Serializable] + public class CreditCardDetails + { + public DateTime ValidTo { get; set; } + public WireEncryptedString Number { get; set; } + } + + [Serializable] + public class MySecretSubProperty + { + public WireEncryptedString Secret { get; set; } + } + + public class ConfigureEncryption: IProvideConfiguration + { + RijndaelEncryptionServiceConfig rijndaelEncryptionServiceConfig = new RijndaelEncryptionServiceConfig { Key = "gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6" }; + + public RijndaelEncryptionServiceConfig GetConfiguration() + { + return rijndaelEncryptionServiceConfig; + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_custom_service.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_custom_service.cs new file mode 100644 index 000000000..136bbd5f1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_custom_service.cs @@ -0,0 +1,75 @@ +namespace NServiceBus.AcceptanceTests.Encryption +{ + using System.Linq; + using NServiceBus.Encryption; + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_using_encryption_with_custom_service : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_decrypted_message() + { + Scenario.Define() + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MessageWithSecretData + { + Secret = "betcha can't guess my secret" + }))) + .Done(c => c.Done) + .Repeat(r => r.For()) + .Should(c => Assert.AreEqual("betcha can't guess my secret", c.Secret)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + public string Secret { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => c.Configurer.RegisterSingleton(new MyEncryptionService())); + } + + public class Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageWithSecretData message) + { + Context.Secret = message.Secret.Value; + Context.Done = true; + } + } + } + + [Serializable] + public class MessageWithSecretData : IMessage + { + public WireEncryptedString Secret { get; set; } + } + + + public class MyEncryptionService : IEncryptionService + { + public EncryptedValue Encrypt(string value) + { + return new EncryptedValue + { + EncryptedBase64Value = new string(value.Reverse().ToArray()) + }; + } + + public string Decrypt(EncryptedValue encryptedValue) + { + return new string(encryptedValue.EncryptedBase64Value.Reverse().ToArray()); + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_multikey.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_multikey.cs new file mode 100644 index 000000000..60dbf90ce --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Encryption/When_using_encryption_with_multikey.cs @@ -0,0 +1,110 @@ +namespace NServiceBus.AcceptanceTests.Encryption +{ + using System; + using Config; + using Config.ConfigurationSource; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_using_encryption_with_multikey : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_decrypted_message() + { + Scenario.Define() + .WithEndpoint(b => b.Given((bus, context) => bus.Send(new MessageWithSecretData + { + Secret = "betcha can't guess my secret", + }))) + .WithEndpoint() + .Done(c => c.Done) + .Repeat(r => r.For()) + .Should(c => Assert.AreEqual("betcha can't guess my secret", c.Secret)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool Done { get; set; } + + public string Secret { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup(c => c.RijndaelEncryptionService()) + .AddMapping(typeof(Receiver)); + } + + public class Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageWithSecretData message) + { + Context.Secret = message.Secret.Value; + Context.Done = true; + } + } + + public class ConfigureEncryption : IProvideConfiguration + { + public RijndaelEncryptionServiceConfig GetConfiguration() + { + return new RijndaelEncryptionServiceConfig + { + Key = "gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6" + }; + } + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + EndpointSetup(c => c.RijndaelEncryptionService()); + } + + public class Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageWithSecretData message) + { + Context.Secret = message.Secret.Value; + Context.Done = true; + } + } + + public class ConfigureEncryption : IProvideConfiguration + { + public RijndaelEncryptionServiceConfig GetConfiguration() + { + return new RijndaelEncryptionServiceConfig + { + Key = "adDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6", + ExpiredKeys = new RijndaelExpiredKeyCollection + { + new RijndaelExpiredKey + { + Key = "gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6" + } + } + }; + } + } + } + + [Serializable] + public class MessageWithSecretData : IMessage + { + public WireEncryptedString Secret { get; set; } + } + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ConfigureExtensions.cs b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ConfigureExtensions.cs new file mode 100644 index 000000000..3efb0fb63 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ConfigureExtensions.cs @@ -0,0 +1,187 @@ +namespace NServiceBus.AcceptanceTests.EndpointTemplates +{ + using System; + using System.Collections.Generic; + using ObjectBuilder.Common; + using ObjectBuilder.Common.Config; + using Persistence.InMemory.SagaPersister; + using Persistence.InMemory.SubscriptionStorage; + using Persistence.InMemory.TimeoutPersister; + using Persistence.Msmq.SubscriptionStorage; + using Persistence.Raven.SagaPersister; + using Persistence.Raven.SubscriptionStorage; + using Persistence.Raven.TimeoutPersister; + using ScenarioDescriptors; + using Serializers.Binary; + using Serializers.Json; + using Serializers.XML; + + public static class ConfigureExtensions + { + public static string GetOrNull(this IDictionary dictionary, string key) + { + if (!dictionary.ContainsKey(key)) + { + return null; + } + + return dictionary[key]; + } + + public static Configure DefineTransport(this Configure config, IDictionary settings) + { + if (!settings.ContainsKey("Transport")) + { + settings = Transports.Default.Settings; + } + + var transportType = Type.GetType(settings["Transport"]); + + return config.UseTransport(transportType, () => settings["Transport.ConnectionString"]); + } + + public static Configure DefineSerializer(this Configure config, string serializer) + { + if (string.IsNullOrEmpty(serializer)) + { + return config; //xml is the default + } + + var type = Type.GetType(serializer); + + if (type == typeof(XmlMessageSerializer)) + { + Configure.Serialization.Xml(); + return config; + } + + if (type == typeof(JsonMessageSerializer)) + { + Configure.Serialization.Json(); + return config; + } + + if (type == typeof(BsonMessageSerializer)) + { + Configure.Serialization.Bson(); + return config; + } + + if (type == typeof(BinaryMessageSerializer)) + { + Configure.Serialization.Binary(); + return config; + } + + throw new InvalidOperationException("Unknown serializer:" + serializer); + } + + public static Configure DefineTimeoutPersister(this Configure config, string persister) + { + if (string.IsNullOrEmpty(persister)) + { + persister = TimeoutPersisters.Default.Settings["TimeoutPersister"]; + } + + if (persister.Contains(typeof(InMemoryTimeoutPersistence).FullName)) + { + return config.UseInMemoryTimeoutPersister(); + } + + if (persister.Contains(typeof(RavenTimeoutPersistence).FullName)) + { + config.RavenPersistence(() => "url=http://localhost:8080"); + return config.UseRavenTimeoutPersister(); + } + + CallConfigureForPersister(config, persister); + + return config; + } + + public static Configure DefineSagaPersister(this Configure config, string persister) + { + if (string.IsNullOrEmpty(persister)) + { + persister = SagaPersisters.Default.Settings["SagaPersister"]; + } + + if (persister.Contains(typeof(InMemorySagaPersister).FullName)) + { + return config.InMemorySagaPersister(); + } + + if (persister.Contains(typeof(RavenSagaPersister).FullName)) + { + config.RavenPersistence(() => "url=http://localhost:8080"); + return config.RavenSagaPersister(); + } + + CallConfigureForPersister(config, persister); + + return config; + } + + public static Configure DefineSubscriptionStorage(this Configure config, string persister) + { + if (string.IsNullOrEmpty(persister)) + { + persister = SubscriptionPersisters.Default.Settings["SubscriptionStorage"]; + } + + if (persister.Contains(typeof(InMemorySubscriptionStorage).FullName)) + { + return config.InMemorySubscriptionStorage(); + } + + if (persister.Contains(typeof(RavenSubscriptionStorage).FullName)) + { + config.RavenPersistence(() => "url=http://localhost:8080"); + return config.RavenSubscriptionStorage(); + } + + if (persister.Contains(typeof(MsmqSubscriptionStorage).FullName)) + { + return config.MsmqSubscriptionStorage(); + } + + CallConfigureForPersister(config, persister); + + return config; + } + + static void CallConfigureForPersister(Configure config, string persister) + { + var type = Type.GetType(persister); + + var typeName = "Configure" + type.Name; + + var configurerType = Type.GetType(typeName, false); + + if (configurerType == null) + { + return; + } + + var configurer = Activator.CreateInstance(configurerType); + + dynamic dc = configurer; + + dc.Configure(config); + } + + public static Configure DefineBuilder(this Configure config, string builder) + { + if (string.IsNullOrEmpty(builder)) + { + return config.DefaultBuilder(); + } + + var container = (IContainer) Activator.CreateInstance(Type.GetType(builder)); + + ConfigureCommon.With(config, container); + + return config; + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ContextAppender.cs b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ContextAppender.cs new file mode 100644 index 000000000..08d576545 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/ContextAppender.cs @@ -0,0 +1,35 @@ +namespace NServiceBus.AcceptanceTests.EndpointTemplates +{ + using AcceptanceTesting; + using AcceptanceTesting.Support; + using log4net.Appender; + using log4net.Core; + + public class ContextAppender : AppenderSkeleton + { + public ContextAppender(ScenarioContext context, EndpointConfiguration endpointConfiguration) + { + this.context = context; + this.endpointConfiguration = endpointConfiguration; + } + + protected override void Append(LoggingEvent loggingEvent) + { + if (!endpointConfiguration.AllowExceptions && loggingEvent.ExceptionObject != null) + { + lock (context) + { + context.Exceptions += loggingEvent.ExceptionObject + "/n/r"; + } + } + if (loggingEvent.Level >= Level.Warn) + { + context.RecordLog(endpointConfiguration.EndpointName, loggingEvent.Level.ToString(), loggingEvent.RenderedMessage); + } + + } + + ScenarioContext context; + readonly EndpointConfiguration endpointConfiguration; + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/DefaultServer.cs b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/DefaultServer.cs new file mode 100644 index 000000000..bd23f7021 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/EndpointTemplates/DefaultServer.cs @@ -0,0 +1,84 @@ +namespace NServiceBus.AcceptanceTests.EndpointTemplates +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using AcceptanceTesting.Support; + using Config.ConfigurationSource; + using Hosting.Helpers; + using NServiceBus; + using Settings; + + public class DefaultServer : IEndpointSetupTemplate + { + public Configure GetConfiguration(RunDescriptor runDescriptor, EndpointConfiguration endpointConfiguration, IConfigurationSource configSource) + { + var settings = runDescriptor.Settings; + + SetLoggingLibrary.Log4Net(null, new ContextAppender(runDescriptor.ScenarioContext, endpointConfiguration)); + + var types = GetTypesToUse(endpointConfiguration); + + var transportToUse = settings.GetOrNull("Transport"); + + Configure.Features.Enable(); + + SettingsHolder.SetDefault("ScaleOut.UseSingleBrokerQueue", true); + + var config = Configure.With(types) + .DefineEndpointName(endpointConfiguration.EndpointName) + .CustomConfigurationSource(configSource) + .DefineBuilder(settings.GetOrNull("Builder")) + .DefineSerializer(settings.GetOrNull("Serializer")) + .DefineTransport(settings) + .DefineSagaPersister(settings.GetOrNull("SagaPersister")); + + if (transportToUse == null || + transportToUse.Contains("Msmq") || + transportToUse.Contains("SqlServer") || + transportToUse.Contains("RabbitMq")) + { + config.DefineTimeoutPersister(settings.GetOrNull("TimeoutPersister")); + } + + if (transportToUse == null || transportToUse.Contains("Msmq") || transportToUse.Contains("SqlServer")) + { + config.DefineSubscriptionStorage(settings.GetOrNull("SubscriptionStorage")); + } + + return config.UnicastBus(); + } + + static IEnumerable GetTypesToUse(EndpointConfiguration endpointConfiguration) + { + var assemblies = new AssemblyScanner().GetScannableAssemblies(); + + var types = assemblies.Assemblies + //exclude all test types by default + .Where(a => a != Assembly.GetExecutingAssembly()) + .SelectMany(a => a.GetTypes()); + + + types = types.Union(GetNestedTypeRecursive(endpointConfiguration.BuilderType.DeclaringType, endpointConfiguration.BuilderType)); + + types = types.Union(endpointConfiguration.TypesToInclude); + + return types.Where(t => !endpointConfiguration.TypesToExclude.Contains(t)).ToList(); + } + + static IEnumerable GetNestedTypeRecursive(Type rootType, Type builderType) + { + yield return rootType; + + if (typeof(IEndpointConfigurationFactory).IsAssignableFrom(rootType) && rootType != builderType) + yield break; + + foreach (var nestedType in rootType.GetNestedTypes(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SelectMany(t => GetNestedTypeRecursive(t, builderType))) + { + yield return nestedType; + } + } + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/SerializerCorrupter.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/SerializerCorrupter.cs new file mode 100644 index 000000000..80ccecc78 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/SerializerCorrupter.cs @@ -0,0 +1,17 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Reflection; + + static class SerializerCorrupter + { + + public static void Corrupt() + { + var msmqUtilitiesType = Type.GetType("NServiceBus.Transports.Msmq.MsmqUtilities, NServiceBus.Core"); + var headerSerializerField = msmqUtilitiesType.GetField("headerSerializer", BindingFlags.Static | BindingFlags.NonPublic); + headerSerializerField.SetValue(null, null); + } + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage.cs new file mode 100644 index 000000000..a5e0adddc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage.cs @@ -0,0 +1,56 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Linq; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NUnit.Framework; + + public class When_cant_convert_to_TransportMessage : NServiceBusAcceptanceTest + { + [Test] + public void Should_send_message_to_error_queue() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.Send(new Message()))) + .WithEndpoint() + .Done(c => c.GetAllLogs().Any(l => l.Level == "error")) + .Repeat(r => r.For(ScenarioDescriptors.Transports.Msmq)) + .Should(c => + { + var logs = c.GetAllLogs(); + Assert.True(logs.Any(l => l.Message.Contains("is corrupt and will be moved to"))); + }) + .Run(); + } + + public class Context : ScenarioContext + { + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup() + .AddMapping(typeof(Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + SerializerCorrupter.Corrupt(); + EndpointSetup() + .AllowExceptions(); + } + + } + + [Serializable] + public class Message : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_NoTransactions.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_NoTransactions.cs new file mode 100644 index 000000000..39936aff5 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_NoTransactions.cs @@ -0,0 +1,58 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Linq; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NUnit.Framework; + using IMessage = NServiceBus.IMessage; + + public class When_cant_convert_to_TransportMessage_NoTransactions : NServiceBusAcceptanceTest + { + [Test] + public void Should_send_message_to_error_queue() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.Send(new Message()))) + .WithEndpoint() + .Done(c => c.GetAllLogs().Any(l => l.Level == "error")) + .Repeat(r => r.For(ScenarioDescriptors.Transports.Msmq)) + .Should(c => + { + var logs = c.GetAllLogs(); + Assert.True(logs.Any(l => l.Message.Contains("is corrupt and will be moved to"))); + }) + .Run(); + } + + public class Context : ScenarioContext + { + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + Configure.Transactions.Disable(); + EndpointSetup() + .AddMapping(typeof(Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + SerializerCorrupter.Corrupt(); + Configure.Transactions.Disable(); + EndpointSetup() + .AllowExceptions(); + } + } + + [Serializable] + public class Message : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_SuppressedDTC.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_SuppressedDTC.cs new file mode 100644 index 000000000..77ba20bc7 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/Cant_convert_to_TransportMessage/When_cant_convert_to_TransportMessage_SuppressedDTC.cs @@ -0,0 +1,57 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Linq; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NUnit.Framework; + + public class When_cant_convert_to_TransportMessage_SuppressedDTC : NServiceBusAcceptanceTest + { + [Test] + public void Should_send_message_to_error_queue() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.Send(new Message()))) + .WithEndpoint() + .Done(c => c.GetAllLogs().Any(l => l.Level == "error")) + .Repeat(r => r.For(ScenarioDescriptors.Transports.Msmq)) + .Should(c => + { + var logs = c.GetAllLogs(); + Assert.True(logs.Any(l => l.Message.Contains("is corrupt and will be moved to"))); + }) + .Run(); + } + + public class Context : ScenarioContext + { + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + Configure.Transactions.Advanced(settings => settings.DisableDistributedTransactions()); + EndpointSetup() + .AddMapping(typeof(Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + SerializerCorrupter.Corrupt(); + Configure.Transactions.Advanced(settings => settings.DisableDistributedTransactions()); + EndpointSetup() + .AllowExceptions(); + } + } + + [Serializable] + public class Message : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/StackTraceAssert.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/StackTraceAssert.cs new file mode 100644 index 000000000..a6293edae --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/StackTraceAssert.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; + +namespace NServiceBus.AcceptanceTests.Exceptions +{ + static class StackTraceAssert + { +// ReSharper disable once UnusedParameter.Global + public static void StartsWith(string expected, string actual) + { + if (actual == null) + { + Assert.Fail(); + } + else + { + var cleanStackTrace = CleanStackTrace(actual); + try + { + Assert.IsTrue(cleanStackTrace.Replace("\r\n", "\n").StartsWith(expected.Replace("\r\n", "\n"))); + } + catch (Exception) + { + Trace.WriteLine(cleanStackTrace); + throw; + } + } + } + static string CleanStackTrace(string stackTrace) + { + if (stackTrace== null) + { + return string.Empty; + } + using (var stringReader = new StringReader(stackTrace)) + { + var stringBuilder = new StringBuilder(); + while (true) + { + var line = stringReader.ReadLine(); + if (line == null) + { + break; + } + + stringBuilder.AppendLine(line.Split(new[] + { + " in " + }, StringSplitOptions.RemoveEmptyEntries).First().Trim()); + } + return stringBuilder.ToString().Trim(); + } + } + } +} + diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_and_different_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_and_different_End_throws.cs new file mode 100644 index 000000000..3bd98448b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_and_different_End_throws.cs @@ -0,0 +1,153 @@ +#pragma warning disable 0618 +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NUnit.Framework; + + public class When_Module_Begin_and_different_End_throws : NServiceBusAcceptanceTest + { + [Test] + [Explicit] + public void Should_receive_AggregateException_with_both_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + var exceptionType = context.ExceptionType; + Assert.AreEqual(typeof(AggregateException), exceptionType); + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + public string Log { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class MessageModuleThatThrowsInBegin : IMessageModule + { + public Context Context { get; set; } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + Context.Log += "ThrowsInBegin Begin\r\n"; + throw new BeginException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + Context.Log += "ThrowsInBegin Begin\r\n"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + Context.Log += "ThrowsInBegin Begin\r\n"; + } + } + + class MessageModuleThatThrowsInEnd : IMessageModule + { + public Context Context { get; set; } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + Context.Log += "ThrowsInEnd Begin\r\n"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + Context.Log += "ThrowsInEnd End\r\n"; + throw new EndException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + Context.Log += "ThrowsInEnd Error\r\n"; + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class BeginException : Exception + { + public BeginException() + : base("BeginException") + { + + } + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_throws.cs new file mode 100644 index 000000000..c8c81c09e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Begin_throws.cs @@ -0,0 +1,121 @@ +#pragma warning disable 0618 +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NUnit.Framework; + + public class When_Module_Begin_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_from_begin() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(BeginException), context.ExceptionType); +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_Module_Begin_throws.Endpoint.MessageModuleThatThrowsInBegin.HandleBeginMessage() +//at System.Collections.Generic.List`1.ForEach(Action`1 action) +//at NServiceBus.Unicast.UnicastBus.TransportStartedMessageProcessing(Object sender, StartedMessageProcessingEventArgs e) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class MessageModuleThatThrowsInBegin:IMessageModule + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + throw new BeginException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class BeginException : Exception + { + public BeginException() + : base("BeginException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_End_throws.cs new file mode 100644 index 000000000..a69f566ec --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_End_throws.cs @@ -0,0 +1,121 @@ +#pragma warning disable 0618 +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NUnit.Framework; + + public class When_Module_End_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_from_end() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(EndException), context.ExceptionType); +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_Module_End_throws.Endpoint.MessageModuleThatThrowsInEnd.HandleEndMessage() +//at System.Collections.Generic.List`1.ForEach(Action`1 action) +//at NServiceBus.Unicast.UnicastBus.TransportFinishedMessageProcessing(Object sender, FinishedMessageProcessingEventArgs e) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class MessageModuleThatThrowsInEnd:IMessageModule + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + throw new EndException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Error_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Error_throws.cs new file mode 100644 index 000000000..dda7ebe3c --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Module_Error_throws.cs @@ -0,0 +1,122 @@ +#pragma warning disable 0618 +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NUnit.Framework; + + public class When_Module_Error_throws : NServiceBusAcceptanceTest + { + [Test] + [Explicit] + public void Should_receive_AggregateException_with_both_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + Assert.AreEqual(typeof(AggregateException), context.ExceptionType); + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class MessageModuleThatThrowsInBegin:IMessageModule + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + throw new ErrorException(); + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + throw new HandlerException(); + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class ErrorException : Exception + { + public ErrorException() + : base("ErrorException") + { + + } + } + public class HandlerException : Exception + { + public HandlerException() + : base("HandlerException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_and_different_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_and_different_End_throws.cs new file mode 100644 index 000000000..01dd8c78c --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_and_different_End_throws.cs @@ -0,0 +1,194 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_Uow_Begin_and_different_End_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_AggregateException_with_both_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(BeginException), context.InnerExceptionOneType); + Assert.AreEqual(typeof(EndException), context.InnerExceptionTwoType); + + +//#if (!DEBUG) + +// StackTraceAssert.StartsWith( +//@"at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); + +// var expected = string.Format(@"at NServiceBus.AcceptanceTests.Exceptions.When_Uow_Begin_and_different_End_throws.Endpoint.{0}.End(Exception ex) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.AppendEndExceptionsAndRethrow(Exception initialException)", context.TypeThatThrewInEnd); +// StackTraceAssert.StartsWith(expected, context.InnerExceptionTwoStackTrace); + +//#endif + + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + public string InnerExceptionOneStackTrace { get; set; } + public string InnerExceptionTwoStackTrace { get; set; } + public Type InnerExceptionOneType { get; set; } + public Type InnerExceptionTwoType { get; set; } + public bool FirstOneExecuted { get; set; } + public string TypeThatThrewInEnd { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + var aggregateException = (AggregateException)e; + Context.StackTrace = aggregateException.StackTrace; + var innerExceptions = aggregateException.InnerExceptions; + Context.InnerExceptionOneStackTrace = innerExceptions[0].StackTrace; + Context.InnerExceptionTwoStackTrace = innerExceptions[1].StackTrace; + Context.InnerExceptionOneType = innerExceptions[0].GetType(); + Context.InnerExceptionTwoType = innerExceptions[1].GetType(); + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + public class UnitOfWorkThatThrows1 : IManageUnitsOfWork + { + public Context Context { get; set; } + + bool throwAtEnd; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + if (Context.FirstOneExecuted) + { + throw new BeginException(); + } + + Context.FirstOneExecuted = throwAtEnd = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + if (throwAtEnd) + { + Context.TypeThatThrewInEnd = GetType().Name; + + throw new EndException(); + } + } + } + public class UnitOfWorkThatThrows2 : IManageUnitsOfWork + { + public Context Context { get; set; } + + bool throwAtEnd; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + if (Context.FirstOneExecuted) + { + throw new BeginException(); + } + + Context.FirstOneExecuted = throwAtEnd = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + if (throwAtEnd) + { + Context.TypeThatThrewInEnd = GetType().Name; + + throw new EndException(); + } + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class BeginException : Exception + { + public BeginException() + : base("BeginException") + { + + } + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_throws.cs new file mode 100644 index 000000000..1df9f6c6c --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_Begin_throws.cs @@ -0,0 +1,121 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_Uow_Begin_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_thrown_from_begin() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(BeginException), context.ExceptionType); +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_Uow_Begin_throws.Endpoint.UnitOfWorkThatThrowsInBegin.Begin() +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + public class UnitOfWorkThatThrowsInBegin : IManageUnitsOfWork + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + throw new BeginException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + } + + [Serializable] + public class Message : IMessage + { + } + public class BeginException : Exception + { + public BeginException() + : base("BeginException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_End_throws.cs new file mode 100644 index 000000000..2e041809e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_Uow_End_throws.cs @@ -0,0 +1,120 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_Uow_End_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_thrown_from_end() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(EndException), context.ExceptionType); +//#if(!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_Uow_End_throws.Endpoint.UnitOfWorkThatThrowsInEnd.End(Exception ex) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class UnitOfWorkThatThrowsInEnd : IManageUnitsOfWork + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + throw new EndException(); + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + } + + [Serializable] + public class Message : IMessage + { + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Module_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Module_End_throws.cs new file mode 100644 index 000000000..237e94062 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Module_End_throws.cs @@ -0,0 +1,133 @@ +#pragma warning disable 0618 +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NUnit.Framework; + + public class When_handler_and_Module_End_throws : NServiceBusAcceptanceTest + { + [Test] + [Explicit] + public void Should_receive_AggregateException_with_both_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + var exceptionType = context.ExceptionType; + Assert.AreEqual(typeof(AggregateException), exceptionType); + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + public string InnerExceptionOneStackTrace { get; set; } + public string InnerExceptionTwoStackTrace { get; set; } + public Type InnerExceptionOneType { get; set; } + public Type InnerExceptionTwoType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + var aggregateException = (AggregateException)e; + Context.StackTrace = aggregateException.StackTrace; + var exceptions = aggregateException.InnerExceptions; + Context.InnerExceptionOneStackTrace = exceptions[0].StackTrace; + Context.InnerExceptionTwoStackTrace = exceptions[1].StackTrace; + Context.InnerExceptionOneType = exceptions[0].GetType(); + Context.InnerExceptionTwoType = exceptions[1].GetType(); + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + class MessageModuleThatThrowsInBegin:IMessageModule + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleBeginMessage() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleEndMessage() + { + throw new EndException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void HandleError() + { + } + } + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + throw new HandlerException(); + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + public class HandlerException : Exception + { + public HandlerException() + : base("HandlerException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Uow_End_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Uow_End_throws.cs new file mode 100644 index 000000000..7093b10e0 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_and_Uow_End_throws.cs @@ -0,0 +1,211 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NServiceBus.UnitOfWork; + using NUnit.Framework; + + public class When_handler_and_Uow_End_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_AggregateException_with_both_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(HandlerException), context.InnerExceptionOneType); + Assert.AreEqual(typeof(EndException), context.InnerExceptionTwoType); + +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); + +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_handler_and_Uow_End_throws.Endpoint.Handler.Handle(Message message) +//at NServiceBus.Unicast.HandlerInvocationCache.Invoke(Object handler, Object message, Dictionary`2 dictionary) +//at NServiceBus.Unicast.Behaviors.InvokeHandlersBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.SagaPersistenceBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.AuditInvokedSagaBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Unicast.Behaviors.SetCurrentMessageBeingHandledBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Behaviors.LoadHandlersBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.DataBus.DataBusReceiveBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.MessageMutator.ApplyIncomingMessageMutatorsBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Messages.ExecuteLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.CallbackInvocationBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Messages.ExtractLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Sagas.RemoveIncomingHeadersBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.MessageMutator.ApplyIncomingTransportMessageMutatorsBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next)", context.InnerExceptionOneStackTrace); + +// StackTraceAssert.StartsWith( +//string.Format(@"at NServiceBus.AcceptanceTests.Exceptions.When_handler_and_Uow_End_throws.Endpoint.UnitOfWorkThatThrows1.End(Exception ex) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.AppendEndExceptionsAndRethrow(Exception initialException)", context.TypeName), context.InnerExceptionTwoStackTrace); + +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public string InnerExceptionOneStackTrace { get; set; } + public string InnerExceptionTwoStackTrace { get; set; } + public Type InnerExceptionOneType { get; set; } + public Type InnerExceptionTwoType { get; set; } + public bool FirstOneExecuted { get; set; } + public string TypeName { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + var aggregateException = (AggregateException)e; + Context.StackTrace = aggregateException.StackTrace; + var exceptions = aggregateException.InnerExceptions; + Context.InnerExceptionOneStackTrace = exceptions[0].StackTrace; + Context.InnerExceptionTwoStackTrace = exceptions[1].StackTrace; + Context.InnerExceptionOneType = exceptions[0].GetType(); + Context.InnerExceptionTwoType = exceptions[1].GetType(); + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + + } + } + + public class UnitOfWorkThatThrows1 : IManageUnitsOfWork + { + public Context Context { get; set; } + + bool executedInSecondPlace; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + if (Context.FirstOneExecuted) + { + executedInSecondPlace = true; + } + + Context.FirstOneExecuted = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + if (executedInSecondPlace) + { + Context.TypeName = GetType().Name; + + throw new EndException(); + } + } + } + + public class UnitOfWorkThatThrows2 : IManageUnitsOfWork + { + public Context Context { get; set; } + + bool executedInSecondPlace; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Begin() + { + if (Context.FirstOneExecuted) + { + executedInSecondPlace = true; + } + + Context.FirstOneExecuted = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void End(Exception ex = null) + { + if (executedInSecondPlace) + { + Context.TypeName = GetType().Name; + + throw new EndException(); + } + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + throw new HandlerException(); + } + } + + } + + [Serializable] + public class Message : IMessage + { + } + public class HandlerException : Exception + { + public HandlerException() + : base("HandlerException") + { + + } + } + public class EndException : Exception + { + public EndException() + : base("EndException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws.cs new file mode 100644 index 000000000..58473cd5b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws.cs @@ -0,0 +1,119 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NUnit.Framework; + + public class When_handler_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_from_handler() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + Assert.AreEqual(typeof(HandlerException), context.ExceptionType); +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_handler_throws.Endpoint.Handler.Handle(Message message) +//at NServiceBus.Unicast.HandlerInvocationCache.Invoke(Object handler, Object message, Dictionary`2 dictionary) +//at NServiceBus.Unicast.Behaviors.InvokeHandlersBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.SagaPersistenceBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.AuditInvokedSagaBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Unicast.Behaviors.SetCurrentMessageBeingHandledBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Behaviors.LoadHandlersBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.DataBus.DataBusReceiveBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.MessageMutator.ApplyIncomingMessageMutatorsBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Messages.ExecuteLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.CallbackInvocationBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Messages.ExtractLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Sagas.RemoveIncomingHeadersBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.MessageMutator.ApplyIncomingTransportMessageMutatorsBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + throw new HandlerException(); + } + } + } + + [Serializable] + public class Message : IMessage + { + } + public class HandlerException : Exception + { + public HandlerException() + : base("HandlerException") + { + + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_AggregateException.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_AggregateException.cs new file mode 100644 index 000000000..911f29855 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_AggregateException.cs @@ -0,0 +1,142 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NUnit.Framework; + + public class When_handler_throws_AggregateException : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exact_AggregateException_exception_from_handler() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + Assert.AreEqual(typeof(AggregateException), context.ExceptionType); + Assert.AreEqual(typeof(Exception), context.InnerExceptionType); + Assert.AreEqual("My Exception", context.ExceptionMessage); + Assert.AreEqual("My Inner Exception", context.InnerExceptionMessage); + +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +// @"at NServiceBus.AcceptanceTests.Exceptions.When_handler_throws_AggregateException.Endpoint.Handler.Handle(Message message) +//at NServiceBus.Unicast.HandlerInvocationCache.Invoke(Object handler, Object message, Dictionary`2 dictionary) +//at NServiceBus.Unicast.Behaviors.InvokeHandlersBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.SagaPersistenceBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.AuditInvokedSagaBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Unicast.Behaviors.SetCurrentMessageBeingHandledBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Behaviors.LoadHandlersBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.DataBus.DataBusReceiveBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.MessageMutator.ApplyIncomingMessageMutatorsBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Messages.ExecuteLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.CallbackInvocationBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Messages.ExtractLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Sagas.RemoveIncomingHeadersBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.MessageMutator.ApplyIncomingTransportMessageMutatorsBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); + +// StackTraceAssert.StartsWith( +// @"at NServiceBus.AcceptanceTests.Exceptions.When_handler_throws_AggregateException.Endpoint.Handler.MethodThatThrows() +//at NServiceBus.AcceptanceTests.Exceptions.When_handler_throws_AggregateException.Endpoint.Handler.Handle(Message message)", context.InnerStackTrace); +//#endif + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public string InnerStackTrace { get; set; } + public Type InnerExceptionType { get; set; } + public string ExceptionMessage { get; set; } + public string InnerExceptionMessage { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionMessage = e.Message; + Context.StackTrace = e.StackTrace; + Context.ExceptionType = e.GetType(); + if (e.InnerException != null) + { + Context.InnerExceptionMessage = e.InnerException.Message; + Context.InnerExceptionType = e.InnerException.GetType(); + Context.InnerStackTrace = e.InnerException.StackTrace; + } + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + try + { + MethodThatThrows(); + } + catch (Exception exception) + { + throw new AggregateException("My Exception", exception); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + void MethodThatThrows() + { + throw new Exception("My Inner Exception"); + } + } + } + + [Serializable] + public class Message : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_with_Dispatcher.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_with_Dispatcher.cs new file mode 100644 index 000000000..2f7db74ac --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_handler_throws_with_Dispatcher.cs @@ -0,0 +1,139 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Config; + using NServiceBus.ObjectBuilder; + using NServiceBus.Unicast; + using NUnit.Framework; + + public class When_handler_throws_with_Dispatcher : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_exception_from_handler() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + Assert.AreEqual(typeof(HandlerException), context.ExceptionType); +//#if (!DEBUG) +// StackTraceAssert.StartsWith( +//@"at NServiceBus.AcceptanceTests.Exceptions.When_handler_throws_with_Dispatcher.Endpoint.Handler.Handle(Message message) +//at NServiceBus.Unicast.Behaviors.InvokeHandlersBehavior.<>c__DisplayClass2.b__0(Action dispatch) +//at System.Collections.Generic.List`1.ForEach(Action`1 action) +//at NServiceBus.Unicast.Behaviors.InvokeHandlersBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.SagaPersistenceBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Sagas.AuditInvokedSagaBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Unicast.Behaviors.SetCurrentMessageBeingHandledBehavior.Invoke(HandlerInvocationContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Behaviors.LoadHandlersBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.DataBus.DataBusReceiveBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.MessageMutator.ApplyIncomingMessageMutatorsBehavior.Invoke(ReceiveLogicalMessageContext context, Action next) +//at NServiceBus.Pipeline.PipelineExecutor.Execute[T](BehaviorChain`1 pipelineAction, T context) +//at NServiceBus.Unicast.Messages.ExecuteLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.CallbackInvocationBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Messages.ExtractLogicalMessagesBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Sagas.RemoveIncomingHeadersBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.MessageMutator.ApplyIncomingTransportMessageMutatorsBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.UnitOfWork.UnitOfWorkBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ForwardBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Audit.AuditBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ImpersonateSenderBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.MessageHandlingLoggingBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Behaviors.ChildContainerBehavior.Invoke(ReceivePhysicalMessageContext context, Action next) +//at NServiceBus.Unicast.Transport.TransportReceiver.ProcessMessage(TransportMessage message) +//at NServiceBus.Unicast.Transport.TransportReceiver.TryProcess(TransportMessage message)", context.StackTrace); +//#endif + + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + Context.ExceptionReceived = true; + } + + public void Init(Address address) + { + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + throw new HandlerException(); + } + } + + class MyDispatcher : IMessageDispatcherFactory + { + [MethodImpl(MethodImplOptions.NoInlining)] + public IEnumerable GetDispatcher(Type messageHandlerType, IBuilder builder, object toHandle) + { + yield return () => new Handler().Handle((Message) toHandle); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public bool CanDispatch(Type handler) + { + return handler == typeof(Handler); + } + } + } + + [Serializable] + public class Message : IMessage + { + } + + public class HandlerException : Exception + { + public HandlerException() + : base("HandlerException") + { + + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_processing_a_message_without_an_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_processing_a_message_without_an_id.cs new file mode 100644 index 000000000..4fd3fa690 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_processing_a_message_without_an_id.cs @@ -0,0 +1,100 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.MessageMutator; + using NServiceBus.Unicast; + using NServiceBus.Unicast.Transport; + using NUnit.Framework; + + public class When_processing_a_message_without_an_id : NServiceBusAcceptanceTest + { + [Test] + [Explicit( "In light of https://github.com/Particular/NServiceBus.SqlServer/issues/82 this will always fail." )] + public void Should_invoke_start_message_processing_listeners() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.StartMessageProcessingCalled) + .Run(); + + Assert.IsTrue(context.StartMessageProcessingCalled); + } + + public class Context : ScenarioContext + { + public bool StartMessageProcessingCalled { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerCall); + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class StartProcessingListener : IWantToRunWhenBusStartsAndStops + { + Context context; + + public StartProcessingListener(UnicastBus bus, Context context) + { + this.context = context; + bus.Transport.StartedMessageProcessing += transport_StartedMessageProcessing; + } + + void transport_StartedMessageProcessing(object sender, StartedMessageProcessingEventArgs e) + { + context.StartMessageProcessingCalled = true; + } + + public void Start() + { + } + + public void Stop() + { + } + } + + class CorruptionMutator : IMutateTransportMessages + { + public void MutateIncoming(TransportMessage transportMessage) + { + } + + public void MutateOutgoing(object[] messages, TransportMessage transportMessage) + { + transportMessage.Headers[Headers.MessageId] = ""; + } + } + + class Handler : IHandleMessages + { + public void Handle(Message message) + { + } + } + } + + [Serializable] + public class Message : IMessage + { + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_serialization_throws.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_serialization_throws.cs new file mode 100644 index 000000000..3593d51fc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Exceptions/When_serialization_throws.cs @@ -0,0 +1,108 @@ +namespace NServiceBus.AcceptanceTests.Exceptions +{ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.Serialization; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Config; + using NServiceBus.Faults; + using NServiceBus.MessageMutator; + using NUnit.Framework; + + public class When_serialization_throws : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_SerializationException() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new Message()))) + .Done(c => c.ExceptionReceived) + .Run(); + + Assert.AreEqual(typeof(SerializationException), context.ExceptionType); + } + + public class Context : ScenarioContext + { + public bool ExceptionReceived { get; set; } + public string StackTrace { get; set; } + public Type ExceptionType { get; set; } + public string InnerExceptionStackTrace { get; set; } + public Type InnerExceptionType { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerCall); + c.DisableTimeoutManager(); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + Context.ExceptionType = e.GetType(); + Context.StackTrace = e.StackTrace; + if (e.InnerException != null) + { + Context.InnerExceptionType = e.InnerException.GetType(); + Context.InnerExceptionStackTrace = e.InnerException.StackTrace; + } + Context.ExceptionReceived = true; + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + } + + public void Init(Address address) + { + } + } + + class CorruptionMutator : IMutateTransportMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void MutateIncoming(TransportMessage transportMessage) + { + transportMessage.Body[1]++; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void MutateOutgoing(object[] messages, TransportMessage transportMessage) + { + } + } + + class Handler : IHandleMessages + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void Handle(Message message) + { + } + } + } + + [Serializable] + public class Message : IMessage + { + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_between_sites.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_between_sites.cs new file mode 100644 index 000000000..284c15535 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_between_sites.cs @@ -0,0 +1,105 @@ +namespace NServiceBus.AcceptanceTests.Gateway +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_doing_request_response_between_sites : NServiceBusAcceptanceTest + { + [Test, Explicit( "This tests requires URLS ACL to be set on the Agent" )] + public void Callback_should_be_fired() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint( + b => b.Given((bus, c) => + bus.SendToSites(new[] { "SiteB" }, new MyRequest()) + .Register(result => c.GotCallback = true))) + .WithEndpoint() + .Done(c => c.GotCallback) + .Run(TimeSpan.FromSeconds(2000)); + + Assert.IsTrue(context.GotCallback); + } + + public class Context : ScenarioContext + { + public bool GotCallback { get; set; } + } + + public class SiteA : EndpointConfigurationBuilder + { + public SiteA() + { + EndpointSetup(c => c.RunGateway() + .UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Sites = new SiteCollection + { + new SiteConfig + { + Key = "SiteB", + Address = "http://localhost:25799/SiteB/", + ChannelType = "http" + } + }; + + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25799/SiteA/", + ChannelType = "http", + Default = true + } + }; + }); + } + } + + public class SiteB : EndpointConfigurationBuilder + { + public SiteB() + { + EndpointSetup(c => c.RunGateway().UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25799/SiteB/", + ChannelType = "http", + Default = true + } + }; + }); + + } + + public class MyRequestHandler : IHandleMessages + { + public IBus Bus { get; set; } + public void Handle(MyRequest request) + { + Bus.Reply(new MyResponse()); + } + } + } + + [Serializable] + public class MyRequest : ICommand + { + } + [Serializable] + public class MyResponse : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_with_databus_between_sites.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_with_databus_between_sites.cs new file mode 100644 index 000000000..8bdbae38d --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_response_with_databus_between_sites.cs @@ -0,0 +1,143 @@ +namespace NServiceBus.AcceptanceTests.Gateway +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_doing_request_response_with_databus_between_sites : NServiceBusAcceptanceTest + { + static readonly byte[] PayloadToSend = new byte[1024 * 1024 * 10]; + + [Test, Explicit("This tests requires URLS ACL to be set on the Agent")] + public void Should_be_able_to_reply_to_the_message_using_databus() + { + Scenario.Define() + .WithEndpoint( + b => b.Given((bus, context) => + bus.SendToSites(new[] { "SiteB" }, new MyRequest { Payload = new DataBusProperty(PayloadToSend) }) + .Register(result => context.GotCallback = true))) + .WithEndpoint() + .Done(c => c.GotResponseBack && c.GotCallback) + .Repeat(r => r.For(Transports.Default)) + .Should(c => + { + Assert.AreEqual(PayloadToSend, c.SiteBReceivedPayload, + "The large payload should be marshalled correctly using the databus"); + Assert.AreEqual(PayloadToSend, c.SiteAReceivedPayloadInResponse, + "The large payload should be marshalled correctly using the databus"); + Assert.AreEqual(@"http,http://localhost:25899/SiteA/", c.OriginatingSiteForRequest); + Assert.AreEqual(@"http,http://localhost:25899/SiteB/", c.OriginatingSiteForResponse); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool GotResponseBack { get; set; } + public bool GotCallback { get; set; } + public byte[] SiteBReceivedPayload { get; set; } + public byte[] SiteAReceivedPayloadInResponse { get; set; } + public string OriginatingSiteForRequest { get; set; } + public string OriginatingSiteForResponse { get; set; } + } + + public class SiteA : EndpointConfigurationBuilder + { + public SiteA() + { + EndpointSetup(c => c.RunGateway() + .UseInMemoryGatewayPersister() + .FileShareDataBus(@".\databus\siteA")) + .WithConfig(c => + { + c.Sites = new SiteCollection + { + new SiteConfig + { + Key = "SiteB", + Address = "http://localhost:25899/SiteB/", + ChannelType = "http" + } + }; + + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25899/SiteA/", + ChannelType = "http", + Default = true + } + }; + }); + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + + public void Handle(MyResponse response) + { + Context.GotResponseBack = true; + Context.SiteAReceivedPayloadInResponse = response.OriginalPayload.Value; + + // Inspect the headers to find the originating site address + Context.OriginatingSiteForResponse = Bus.CurrentMessageContext.Headers[Headers.OriginatingSite]; + } + } + } + + public class SiteB : EndpointConfigurationBuilder + { + public SiteB() + { + EndpointSetup(c => c.RunGateway().UseInMemoryGatewayPersister() + .FileShareDataBus(@".\databus\siteB")) + .WithConfig(c => + { + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25899/SiteB/", + ChannelType = "http", + Default = true + } + }; + }); + + } + + public class MyRequestHandler : IHandleMessages + { + public IBus Bus { get; set; } + public Context Context { get; set; } + + public void Handle(MyRequest request) + { + Context.SiteBReceivedPayload = request.Payload.Value; + Bus.Reply(new MyResponse {OriginalPayload = request.Payload}); + + // Inspect the headers to find the originating site address + Context.OriginatingSiteForRequest = Bus.CurrentMessageContext.Headers[Headers.OriginatingSite]; + } + } + } + + [Serializable] + public class MyRequest : ICommand + { + public DataBusProperty Payload { get; set; } + } + + [Serializable] + public class MyResponse : IMessage + { + public DataBusProperty OriginalPayload { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_return.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_return.cs new file mode 100644 index 000000000..c740c5867 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_doing_request_return.cs @@ -0,0 +1,101 @@ +namespace NServiceBus.AcceptanceTests.Gateway +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_doing_request_return : NServiceBusAcceptanceTest + { + [Test, Explicit( "This tests requires URLS ACL to be set on the Agent" )] + public void Callback_should_be_fired() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint( + b => b.Given((bus, c) => + bus.SendToSites(new[] { "SiteB" }, new MyRequest()) + .Register(result => c.GotCallback = true))) + .WithEndpoint() + .Done(c => c.GotCallback) + .Run(TimeSpan.FromSeconds(2000)); + + Assert.IsTrue(context.GotCallback); + } + + public class Context : ScenarioContext + { + public bool GotCallback { get; set; } + } + + public class SiteA : EndpointConfigurationBuilder + { + public SiteA() + { + EndpointSetup(c => c.RunGateway() + .UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Sites = new SiteCollection + { + new SiteConfig + { + Key = "SiteB", + Address = "http://localhost:25699/SiteB/", + ChannelType = "http" + } + }; + + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25699/SiteA/", + ChannelType = "http", + Default = true + } + }; + }); + } + } + + public class SiteB : EndpointConfigurationBuilder + { + public SiteB() + { + EndpointSetup(c => c.RunGateway().UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25699/SiteB/", + ChannelType = "http", + Default = true + } + }; + }); + + } + + public class MyRequestHandler : IHandleMessages + { + public IBus Bus { get; set; } + public void Handle(MyRequest request) + { + Bus.Return(1); + } + } + } + + [Serializable] + public class MyRequest : ICommand + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_to_another_site.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_to_another_site.cs new file mode 100644 index 000000000..2ae19c1d9 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_to_another_site.cs @@ -0,0 +1,112 @@ +namespace NServiceBus.AcceptanceTests.Gateway +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_a_message_to_another_site : NServiceBusAcceptanceTest + { + [Test, Explicit( "This tests requires URLS ACL to be set on the Agent" )] + public void Should_be_able_to_reply_to_the_message() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendToSites(new[] { "SiteA" }, new MyRequest()))) + .WithEndpoint() + .Done(c => c.GotResponseBack) + .Repeat(r => r.For(Transports.Default) + ) + .Should(c => Assert.IsTrue(c.GotResponseBack)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool GotResponseBack { get; set; } + } + + public class Headquarters : EndpointConfigurationBuilder + { + public Headquarters() + { + EndpointSetup(c => c.RunGateway().UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Sites = new SiteCollection + { + new SiteConfig + { + Key = "SiteA", + Address = "http://localhost:25899/SiteA/", + ChannelType = "http" + } + }; + + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25899/Headquarters/", + ChannelType = "http" + } + }; + + + }); + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyResponse response) + { + Context.GotResponseBack = true; + } + } + } + + public class SiteA : EndpointConfigurationBuilder + { + public SiteA() + { + EndpointSetup(c => c.RunGateway().UseInMemoryGatewayPersister()) + .AllowExceptions() + .WithConfig(c => + { + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25899/SiteA/", + ChannelType = "http" + } + }; + }); + } + + public class MyRequestHandler : IHandleMessages + { + public IBus Bus { get; set; } + + public void Handle(MyRequest request) + { + Bus.Reply(new MyResponse()); + } + } + } + + [Serializable] + public class MyRequest : IMessage + { + } + + [Serializable] + public class MyResponse : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_via_the_gateway.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_via_the_gateway.cs new file mode 100644 index 000000000..b0ea8ab9e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Gateway/When_sending_a_message_via_the_gateway.cs @@ -0,0 +1,123 @@ +namespace NServiceBus.AcceptanceTests.Gateway +{ + using System; + using System.IO; + using System.Net; + using System.Web; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NServiceBus.Gateway.Utils; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_a_message_via_the_gateway : NServiceBusAcceptanceTest + { + [Test, Explicit( "This tests requires URLS ACL to be set on the Agent" )] + public void Should_process_message() + { + Scenario.Define() + .WithEndpoint(b => b.When(bus => + { + var webRequest = (HttpWebRequest)WebRequest.Create("http://localhost:25898/Headquarters/"); + webRequest.Method = "POST"; + webRequest.ContentType = "text/xml; charset=utf-8"; + webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)"; + + webRequest.Headers.Add("Content-Encoding", "utf-8"); + webRequest.Headers.Add("NServiceBus.CallType", "Submit"); + webRequest.Headers.Add("NServiceBus.AutoAck", "true"); + webRequest.Headers.Add("MySpecialHeader", "MySpecialValue"); + webRequest.Headers.Add("NServiceBus.Id", Guid.NewGuid().ToString("N")); + + const string message = ""; + + using (var messagePayload = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(message))) + { + webRequest.Headers.Add(HttpRequestHeader.ContentMd5, HttpUtility.UrlEncode(Hasher.Hash(messagePayload))); + webRequest.ContentLength = messagePayload.Length; + + using (var requestStream = webRequest.GetRequestStream()) + { + messagePayload.CopyTo(requestStream); + } + } + + while (true) + { + try + { + using (var myWebResponse = (HttpWebResponse) webRequest.GetResponse()) + { + if (myWebResponse.StatusCode == HttpStatusCode.OK) + { + break; + } + } + } + catch (WebException) + { + } + } + })) + .Done(c => c.GotMessage) + .Repeat(r => r.For(Transports.Default)) + .Should(c => + { + Assert.IsTrue(c.GotMessage); + Assert.AreEqual("MySpecialValue", c.MySpecialHeader); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool GotMessage { get; set; } + + public string MySpecialHeader { get; set; } + } + + public class Headquarters : EndpointConfigurationBuilder + { + public Headquarters() + { + EndpointSetup(c => + { + c.RunGateway().UseInMemoryGatewayPersister(); + Configure.Serialization.Xml(); + }) + .IncludeType() + .AllowExceptions() + .WithConfig(c => + { + c.Channels = new ChannelCollection + { + new ChannelConfig + { + Address = "http://localhost:25898/Headquarters/", + ChannelType = "http" + } + }; + }); + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest response) + { + Context.GotMessage = true; + Context.MySpecialHeader = Bus.GetMessageHeader(response, "MySpecialHeader"); + } + } + } + } + + [Serializable] + public class MyRequest : IMessage + { + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/HostInformation/When_a_message_is_received.cs b/src/NServiceBus.SqlServer.AcceptanceTests/HostInformation/When_a_message_is_received.cs new file mode 100644 index 000000000..17658cbba --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/HostInformation/When_a_message_is_received.cs @@ -0,0 +1,74 @@ +namespace NServiceBus.AcceptanceTests.HostInformation +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Unicast; + + public class When_a_message_is_received : NServiceBusAcceptanceTest + { + static Guid hostId = new Guid("39365055-daf2-439e-b84d-acbef8fd803d"); + const string displayName = "FooBar"; + const string displayInstanceIdentifier = "FooBar is great!"; + + [Test] + public void Host_information_should_be_available_in_headers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(e => e.Given(b => b.SendLocal(new MyMessage()))) + .Done(c => c.HostId != Guid.Empty) + .Run(); + + Assert.AreEqual(hostId, context.HostId); + Assert.AreEqual(displayName, context.HostDisplayName); + } + + public class Context : ScenarioContext + { + public Guid HostId { get; set; } + public string HostDisplayName { get; set; } + } + + [Serializable] + public class MyMessage : ICommand + { + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyMessage message) + { + Context.HostId = new Guid(message.GetHeader(Headers.HostId)); + Context.HostDisplayName = message.GetHeader(Headers.HostDisplayName); + } + } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup(); + } + } + + class OverrideHostInformation : IWantToRunWhenConfigurationIsComplete + { + public UnicastBus UnicastBus { get; set; } + + public void Run() + { +#pragma warning disable 618 + var hostInformation = new Hosting.HostInformation(hostId, displayName, displayInstanceIdentifier); +#pragma warning restore 618 + + UnicastBus.HostInformation = hostInformation; + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_calling_DoNotContinueDispatchingCurrentMessageToHandlers_from_inmemory.cs b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_calling_DoNotContinueDispatchingCurrentMessageToHandlers_from_inmemory.cs new file mode 100644 index 000000000..907a5a7dc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_calling_DoNotContinueDispatchingCurrentMessageToHandlers_from_inmemory.cs @@ -0,0 +1,112 @@ +namespace NServiceBus.AcceptanceTests.InMemory +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using Features; + using NUnit.Framework; + +#pragma warning disable 612, 618 + public class When_calling_DoNotContinueDispatchingCurrentMessageToHandlers_from_inmemory : NServiceBusAcceptanceTest + { + [Test] + public void Subsequent_inmemory_handlers_will_not_be_invoked() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new SomeMessage()))) + .Done(c => c.SecondHandlerInvoked) + .Run(); + + Assert.IsTrue(context.FirstHandlerInvoked); + Assert.IsTrue(context.SecondHandlerInvoked); + Assert.IsTrue(context.InMemoryEventReceivedByHandler1); + Assert.IsFalse(context.InMemoryEventReceivedByHandler2); + } + + public class Context : ScenarioContext + { + public bool FirstHandlerInvoked { get; set; } + public bool SecondHandlerInvoked { get; set; } + + public bool InMemoryEventReceivedByHandler1 { get; set; } + public bool InMemoryEventReceivedByHandler2 { get; set; } + + } + + [Serializable] + public class SomeMessage : IMessage { } + + [Serializable] + public class InMemoryEvent : IEvent + { + } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup(c => Configure.Features.Disable()); + } + + class EnsureOrdering : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.Specify(First.Then().AndThen().AndThen()); + } + } + + class FirstHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(SomeMessage message) + { + Context.FirstHandlerInvoked = true; + + Bus.InMemory.Raise(m => { }); + + } + } + + class SecondHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(SomeMessage message) + { + Context.SecondHandlerInvoked = true; + } + } + + class MyEventHandler1 : IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + + public void Handle(InMemoryEvent messageThatIsEnlisted) + { + Bus.DoNotContinueDispatchingCurrentMessageToHandlers(); + + Context.InMemoryEventReceivedByHandler1 = true; + } + } + + class MyEventHandler2 : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(InMemoryEvent messageThatIsEnlisted) + { + Context.InMemoryEventReceivedByHandler2 = true; + } + } + } + } +#pragma warning restore 612, 618 + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event.cs b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event.cs new file mode 100644 index 000000000..b4b50e00f --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event.cs @@ -0,0 +1,83 @@ +namespace NServiceBus.AcceptanceTests.InMemory +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + +#pragma warning disable 612, 618 + public class When_raising_an_in_memory_event : NServiceBusAcceptanceTest + { + [Test] + public void Should_be_delivered_to_handlers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(m => { }))) + .Done(c => c.WasInMemoryEventReceivedByHandler1 && c.WasInMemoryEventReceivedByHandler2) + .Run(); + + Assert.True(context.WasInMemoryEventReceivedByHandler1, "class MyEventHandler1 did not receive the in-memory event"); + Assert.True(context.WasInMemoryEventReceivedByHandler2, "class MyEventHandler2 did not receive the in-memory event"); + } + + public class Context : ScenarioContext + { + public bool WasInMemoryEventReceivedByHandler1 { get; set; } + public bool WasInMemoryEventReceivedByHandler2 { get; set; } + + } + + public class InMemoryEndpoint : EndpointConfigurationBuilder + { + public InMemoryEndpoint() + { + EndpointSetup(c => Configure.Features.Disable()); + } + + public class CommandHandler : IHandleMessages + { + public IBus Bus { get; set; } + public void Handle(SomeCommand messageThatIsEnlisted) + { + Bus.InMemory.Raise(m => { }); + } + } + + public class MyEventHandler1 : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyInMemoryEvent messageThatIsEnlisted) + { + Context.WasInMemoryEventReceivedByHandler1 = true; + } + } + + public class MyEventHandler2 : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyInMemoryEvent messageThatIsEnlisted) + { + Context.WasInMemoryEventReceivedByHandler2 = true; + } + } + } + + + [Serializable] + public class SomeCommand : ICommand + { + } + + [Serializable] + public class MyInMemoryEvent : IEvent + { + } + } +#pragma warning restore 612, 618 + +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_from_a_non_handler.cs b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_from_a_non_handler.cs new file mode 100644 index 000000000..59df41fba --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_from_a_non_handler.cs @@ -0,0 +1,78 @@ +namespace NServiceBus.AcceptanceTests.InMemory +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + +#pragma warning disable 612, 618 + + public class When_raising_an_in_memory_event_from_a_non_handler : NServiceBusAcceptanceTest + { + [Test] + public void Should_be_delivered_to_handlers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint() + .Done(c => c.WasInMemoryEventReceivedByHandler1) + .Run(); + + Assert.True(context.WasInMemoryEventReceivedByHandler1, "class MyEventHandler1 did not receive the in-memory event"); + } + + public class Context : ScenarioContext + { + public bool WasInMemoryEventReceivedByHandler1 { get; set; } + + } + + public class InMemoryEndpoint : EndpointConfigurationBuilder + { + public InMemoryEndpoint() + { + EndpointSetup(c => Configure.Features.Disable()); + } + + public class StartUpRunner : IWantToRunWhenBusStartsAndStops + { + public IBus Bus { get; set; } + + public void Start() + { + Bus.InMemory.Raise(m => m.SetHeader("MyHeader","MyValue")); + } + + public void Stop() + { + } + } + + public class MyEventHandler1 : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyInMemoryEvent message) + { + Assert.AreEqual("MyValue",Headers.GetMessageHeader(message,"MyHeader")); + Context.WasInMemoryEventReceivedByHandler1 = true; + } + } + + } + + + [Serializable] + public class SomeCommand : ICommand + { + } + + [Serializable] + public class MyInMemoryEvent : IEvent + { + } + } +#pragma warning restore 612, 618 +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_transport_mutators_should_not_be_called.cs b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_transport_mutators_should_not_be_called.cs new file mode 100644 index 000000000..fbdc60b8a --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/InMemory/When_raising_an_in_memory_event_transport_mutators_should_not_be_called.cs @@ -0,0 +1,77 @@ +namespace NServiceBus.AcceptanceTests.InMemory +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using MessageMutator; + using NUnit.Framework; + +#pragma warning disable 612, 618 + public class When_raising_an_in_memory_event_transport_mutators_should_not_be_called : NServiceBusAcceptanceTest + { + [Test] + public void Ensure_transport_mutators_should_not_be_called() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.InMemory.Raise(m => { }))) + .Run(); + + Assert.False(context.MutateIncomingCalled); + Assert.False(context.MutateOutGoingCalled); + } + + class Mutator : IMutateTransportMessages, INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.MutateIncomingCalled = true; + } + + public void MutateOutgoing(object[] messages, TransportMessage transportMessage) + { + Context.MutateOutGoingCalled = true; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class Context : ScenarioContext + { + public bool MutateOutGoingCalled { get; set; } + public bool MutateIncomingCalled { get; set; } + } + + public class InMemoryEndpoint : EndpointConfigurationBuilder + { + public InMemoryEndpoint() + { + EndpointSetup(c => Configure.Features.Disable()); + } + + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyInMemoryEvent messageThatIsEnlisted) + { + } + } + + + [Serializable] + public class MyInMemoryEvent : IEvent + { + } + } +#pragma warning restore 612, 618 +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/Issue_1980.cs b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/Issue_1980.cs new file mode 100644 index 000000000..d4fb4cad6 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/Issue_1980.cs @@ -0,0 +1,83 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class Issue_1980 : NServiceBusAcceptanceTest + { + [Test] + public void Run() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new V1Message()))) + .Done(c => c.V2MessageReceived) + .Run(); + + Assert.IsTrue(context.V2MessageReceived); + Assert.IsFalse(context.V1MessageReceived); + } + + public class Context : ScenarioContext + { + public bool V1MessageReceived { get; set; } + public bool V2MessageReceived { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup( + c => c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerCall)); + } + + class MutateIncomingMessages : IMutateIncomingMessages + { + public object MutateIncoming(object message) + { + if (message is V1Message) + { + return new V2Message(); + } + + return message; + } + } + + class V2MessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(V2Message message) + { + Context.V2MessageReceived = true; + } + } + + class V1MessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(V1Message message) + { + Context.V1MessageReceived = true; + } + } + } + + [Serializable] + public class V1Message : ICommand + { + } + + [Serializable] + public class V2Message : ICommand + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_defining_outgoing_message_mutators.cs b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_defining_outgoing_message_mutators.cs new file mode 100644 index 000000000..ebaf68033 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_defining_outgoing_message_mutators.cs @@ -0,0 +1,95 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class When_defining_outgoing_message_mutators : NServiceBusAcceptanceTest + { + [Test] + public void Should_be_applied_to_outgoing_messages() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeMutated()))) + .Done(c => c.MessageProcessed) + .Run(); + + Assert.True(context.TransportMutatorCalled); + Assert.True(context.MessageMutatorCalled); + } + + public class Context : ScenarioContext + { + public bool MessageProcessed { get; set; } + public bool TransportMutatorCalled { get; set; } + public bool MessageMutatorCalled { get; set; } + } + + public class OutgoingMutatorEndpoint : EndpointConfigurationBuilder + { + public OutgoingMutatorEndpoint() + { + EndpointSetup(); + } + + + class MyTransportMessageMutator:IMutateOutgoingTransportMessages,INeedInitialization + { + public void MutateOutgoing(object[] messages, TransportMessage transportMessage) + { + transportMessage.Headers["TransportMutatorCalled"] = true.ToString(); + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + class MyMessageMutator : IMutateOutgoingMessages, INeedInitialization + { + + + public object MutateOutgoing(object message) + { + Headers.SetMessageHeader(message,"MessageMutatorCalled","true"); + + return message; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + + } + + class MessageToBeMutatedHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + + public void Handle(MessageToBeMutated message) + { + Context.TransportMutatorCalled = Bus.CurrentMessageContext.Headers.ContainsKey("TransportMutatorCalled"); + Context.MessageMutatorCalled = Bus.CurrentMessageContext.Headers.ContainsKey("MessageMutatorCalled"); + + Context.MessageProcessed = true; + + } + } + + } + + [Serializable] + public class MessageToBeMutated : ICommand + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_outgoing_mutator_replaces_message_instance.cs b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_outgoing_mutator_replaces_message_instance.cs new file mode 100644 index 000000000..7548c43a8 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/MessageMutators/When_outgoing_mutator_replaces_message_instance.cs @@ -0,0 +1,83 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using MessageMutator; + using NUnit.Framework; + + public class When_outgoing_mutator_replaces_message_instance : NServiceBusAcceptanceTest + { + [Test] + public void Message_sent_should_be_new_instance() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new V1Message()))) + .Done(c => c.V2MessageReceived) + .Run(); + + Assert.IsTrue(context.V2MessageReceived); + Assert.IsFalse(context.V1MessageReceived); + } + + public class Context : ScenarioContext + { + public bool V1MessageReceived { get; set; } + public bool V2MessageReceived { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup( + c => c.Configurer.ConfigureComponent(DependencyLifecycle.InstancePerCall)); + } + + class MutateOutgoingMessages : IMutateOutgoingMessages + { + public object MutateOutgoing(object message) + { + if (message is V1Message) + { + return new V2Message(); + } + + return message; + } + } + + class V2MessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(V2Message message) + { + Context.V2MessageReceived = true; + } + } + + class V1MessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(V1Message message) + { + Context.V1MessageReceived = true; + } + } + } + + [Serializable] + public class V1Message : ICommand + { + } + + [Serializable] + public class V2Message : ICommand + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBus.SqlServer.AcceptanceTests.csproj b/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBus.SqlServer.AcceptanceTests.csproj index 782d904a3..e1e67cc0a 100644 --- a/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBus.SqlServer.AcceptanceTests.csproj +++ b/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBus.SqlServer.AcceptanceTests.csproj @@ -32,33 +32,36 @@ true - - ..\packages\log4net\lib\2.0\log4net.dll + + ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll + True - - ..\packages\NServiceBus.Interfaces\lib\net40\NServiceBus.dll + + ..\packages\NServiceBus.Interfaces.4.7.5\lib\net40\NServiceBus.dll + True - - ..\packages\NServiceBus.AcceptanceTesting\lib\net40\NServiceBus.AcceptanceTesting.dll + + ..\packages\NServiceBus.AcceptanceTesting.4.7.5\lib\net40\NServiceBus.AcceptanceTesting.dll + True - - ..\packages\NServiceBus\lib\net40\NServiceBus.Core.dll + + ..\packages\NServiceBus.4.7.5\lib\net40\NServiceBus.Core.dll + True - - ..\packages\NUnit\lib\nunit.framework.dll + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + - - - {fa1193bf-325c-4201-bb78-484032e09809} @@ -67,10 +70,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + - \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBusAcceptanceTest.cs b/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBusAcceptanceTest.cs new file mode 100644 index 000000000..0936c2c1e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/NServiceBusAcceptanceTest.cs @@ -0,0 +1,32 @@ +namespace NServiceBus.AcceptanceTests +{ + using AcceptanceTesting.Customization; + using NUnit.Framework; + using System; + + /// + /// Base class for all the NSB test that sets up our conventions + /// + [TestFixture] +// ReSharper disable once PartialTypeWithSinglePart + public abstract partial class NServiceBusAcceptanceTest + { + protected const string ConnectionStringWith_test_schema = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True; Queue Schema=nsb"; + protected const string ConnectionStringWith_sender_schema = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True; Queue Schema=sender"; + protected const string ConnectionStringWith_receiver_schema = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True; Queue Schema=receiver"; + + [SetUp] + public void SetUp() + { + Conventions.EndpointNamingConvention= t => + { + var baseNs = typeof (NServiceBusAcceptanceTest).Namespace; + var testName = GetType().Name; + return t.FullName.Replace(baseNs + ".", "").Replace(testName + "+", "") + + "." + System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(testName).Replace("_", ""); + }; + + Conventions.DefaultRunDescriptor = () => ScenarioDescriptors.Transports.Default; + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/FilteringWhatGetsAudited.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/FilteringWhatGetsAudited.cs new file mode 100644 index 000000000..bc69c067e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/FilteringWhatGetsAudited.cs @@ -0,0 +1,138 @@ + +namespace NServiceBus.AcceptanceTests.PipelineExtension +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NServiceBus.Audit; + using NUnit.Framework; + using Pipeline; + using Pipeline.Contexts; + + /// + /// This is a demo on how pipeline overrides can be used to control which messages that gets audited by NServiceBus + /// + public class FilteringWhatGetsAudited : NServiceBusAcceptanceTest + { + [Test] + public void RunDemo() + { + var context = new Context(); + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeAudited()))) + .WithEndpoint() + .Done(c => c.IsMessageHandlingComplete) + .Run(); + + Assert.IsFalse(context.MessageAudited); + } + + + public class UserEndpoint : EndpointConfigurationBuilder + { + + public UserEndpoint() + { + EndpointSetup() + .AuditTo(); + + } + + class MessageToBeAuditedHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageToBeAudited message) + { + MyContext.IsMessageHandlingComplete = true; + } + } + +#pragma warning disable 618 + class MyFilteringAuditBehavior : IBehavior, + IBehavior + { + public MessageAuditer MessageAuditer { get; set; } + + public void Invoke(ReceivePhysicalMessageContext context, Action next) + { + var auditResult = new AuditFilterResult(); + context.Set(auditResult); + next(); + + //note: and rule operating on the raw TransportMessage can be applied here if needed. + // Access to the message is through: context.PhysicalMessage. Eg: context.PhysicalMessage.Headers.ContainsKey("NServiceBus.ControlMessage") + if (auditResult.DoNotAuditMessage) + { + return; + } + MessageAuditer.ForwardMessageToAuditQueue(context.PhysicalMessage); + } + + public void Invoke(ReceiveLogicalMessageContext context, Action next) + { + //filter out messages of type MessageToBeAudited + if (context.LogicalMessage.MessageType == typeof(MessageToBeAudited)) + { + context.Get().DoNotAuditMessage = true; + } + } + + class AuditFilterResult + { + public bool DoNotAuditMessage { get; set; } + } + + + //here we inject our behavior + class AuditFilteringOverride : PipelineOverride + { + public override void Override(BehaviorList behaviorList) + { + //we replace the default audit behavior with out own + behaviorList.Replace(); + } + + public override void Override(BehaviorList behaviorList) + { + //and also hook into to logical receive pipeline to make filtering on message types easier + behaviorList.Add(); + } + } + +#pragma warning restore 618 + } + } + + public class AuditSpy : EndpointConfigurationBuilder + { + + public AuditSpy() + { + EndpointSetup(); + } + + class AuditMessageHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageToBeAudited message) + { + MyContext.MessageAudited = true; + } + } + } + + public class Context : ScenarioContext + { + public bool IsMessageHandlingComplete { get; set; } + public bool MessageAudited { get; set; } + } + + + [Serializable] + public class MessageToBeAudited : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/MutingHandlerExceptions.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/MutingHandlerExceptions.cs new file mode 100644 index 000000000..ca9a2b19e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/MutingHandlerExceptions.cs @@ -0,0 +1,132 @@ + +namespace NServiceBus.AcceptanceTests.PipelineExtension +{ + using System; + using System.Linq; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using Pipeline; + using Pipeline.Contexts; + using Unicast.Behaviors; + + /// + /// This is a demo on how pipeline overrides can be used to control which messages that gets audited by NServiceBus + /// + public class MutingHandlerExceptions : NServiceBusAcceptanceTest + { + [Test] + public void RunDemo() + { + var context = new Context(); + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageThatWillBlowUpButExWillBeMuted()))) + .WithEndpoint() + .Done(c => c.IsMessageHandlingComplete) + .Run(); + + Assert.IsTrue(context.MessageAudited); + } + + + public class EndpointWithCustomExceptionMuting : EndpointConfigurationBuilder + { + + public EndpointWithCustomExceptionMuting() + { + EndpointSetup() + .AuditTo(); + + } + + class Handler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageThatWillBlowUpButExWillBeMuted message) + { + MyContext.IsMessageHandlingComplete = true; + + throw new Exception("Lets filter on this text"); + } + } + +#pragma warning disable 618 + class MyExceptionFilteringBehavior : IBehavior + { + public void Invoke(HandlerInvocationContext context, Action next) + { + try + { + //invoke the handler/rest of the pipeline + next(); + } + catch (AggregateException ex) + { + //modify this to your liking + if (ex.InnerExceptions.First().Message == "Lets filter on this text") + { + return; + } + throw; + } + catch (Exception ex) + { + //modify this to your liking + if (ex.Message == "Lets filter on this text") + { + return; + } + + throw; + } + } + + + //here we inject our behavior + class MyExceptionFilteringOverride : PipelineOverride + { + public override void Override(BehaviorList behaviorList) + { + //add our behavior to the pipeline just before NSB actually calls the handlers + behaviorList.InsertBefore(); + } + } + +#pragma warning restore 618 + + } + } + + public class AuditSpy : EndpointConfigurationBuilder + { + + public AuditSpy() + { + EndpointSetup(); + } + + class AuditMessageHandler : IHandleMessages + { + public Context MyContext { get; set; } + + public void Handle(MessageThatWillBlowUpButExWillBeMuted message) + { + MyContext.MessageAudited = true; + } + } + } + + public class Context : ScenarioContext + { + public bool IsMessageHandlingComplete { get; set; } + public bool MessageAudited { get; set; } + } + + + [Serializable] + public class MessageThatWillBlowUpButExWillBeMuted : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/SkipDeserialization.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/SkipDeserialization.cs new file mode 100644 index 000000000..cc88227d1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PipelineExtension/SkipDeserialization.cs @@ -0,0 +1,81 @@ +namespace NServiceBus.AcceptanceTests.PipelineExtension +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using Pipeline; + using Pipeline.Contexts; + using Unicast.Messages; + + //This is a demo on how the pipeline overrides can be used to create endpoints that doesn't deserialize incoming messages and there by + // allows the user to handle the raw transport message. This replaces the old feature on the UnicastBus where SkipDeserialization could be set to tru + public class SkipDeserialization : NServiceBusAcceptanceTest + { + [Test] + public void RunDemo() + { + Scenario.Define() + .WithEndpoint( + b => b.Given(bus => bus.SendLocal(new SomeMessage()))) + .Done(c => c.GotTheRawMessage) + .Run(); + } + + public class NonSerializingEndpoint : EndpointConfigurationBuilder + { + public NonSerializingEndpoint() + { + EndpointSetup(); + } + + +#pragma warning disable 618 + //first we override the default "extraction" behavior + class MyOverride : PipelineOverride + { + public override void Override(BehaviorList behaviorList) + { + behaviorList.Replace(); + } + } + + //and then we handle the physical message our self + class MyRawMessageHandler:IBehavior + { + public Context Context { get; set; } + + public void Invoke(ReceivePhysicalMessageContext context, Action next) + { + var transportMessage = context.PhysicalMessage; + + Assert.True(transportMessage.Headers[Headers.EnclosedMessageTypes].Contains(typeof(SomeMessage).Name)); + + Context.GotTheRawMessage = true; + } + } +#pragma warning restore 618 + + class ThisHandlerWontGetInvoked:IHandleMessages + { + public void Handle(SomeMessage message) + { + Assert.Fail(); + } + } + } + + public class Context : ScenarioContext + { + public bool GotTheRawMessage { get; set; } + } + + [Serializable] + public class SomeMessage : ICommand + { + + } + } + + +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/Subscriptions.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/Subscriptions.cs new file mode 100644 index 000000000..d14b480ec --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/Subscriptions.cs @@ -0,0 +1,24 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using Features; + using Unicast.Subscriptions; + using Unicast.Subscriptions.MessageDrivenSubscriptions; + + [Serializable] + public class Subscriptions + { + public static Action> OnEndpointSubscribed = actionToPerform => + { + if (Feature.IsEnabled()) + { + Configure.Instance.Builder.Build().ClientSubscribed += + (sender, args) => + { + actionToPerform(args); + }; + } + }; + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_multi_subscribing_to_a_polymorphic_event.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_multi_subscribing_to_a_polymorphic_event.cs new file mode 100644 index 000000000..9b4ad61a5 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_multi_subscribing_to_a_polymorphic_event.cs @@ -0,0 +1,127 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using Features; + using NUnit.Framework; + + public class When_multi_subscribing_to_a_polymorphic_event : NServiceBusAcceptanceTest + { + [Test] + public void Both_events_should_be_delivered() + { + var rootContext = new Context(); + + Scenario.Define(rootContext) + .WithEndpoint(b => b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(args => + { + if (args.MessageType.Contains(typeof(IMyEvent).Name)) + { + context.SubscribedToIMyEvent = true; + } + + if (args.MessageType.Contains(typeof(MyEvent2).Name)) + { + context.SubscribedToMyEvent2 = true; + } + })) + .When(c => c.SubscribedToIMyEvent && c.SubscribedToMyEvent2, bus => bus.Publish(new MyEvent1()))) + .WithEndpoint(b => b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(args => + { + if (args.MessageType.Contains(typeof(IMyEvent).Name)) + { + context.SubscribedToIMyEvent = true; + } + + if (args.MessageType.Contains(typeof(MyEvent2).Name)) + { + context.SubscribedToMyEvent2 = true; + } + })) + .When(c => c.SubscribedToIMyEvent && c.SubscribedToMyEvent2, bus => bus.Publish(new MyEvent2()))) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + bus.Subscribe(); + + if (!Feature.IsEnabled()) + { + context.SubscribedToIMyEvent = true; + context.SubscribedToMyEvent2 = true; + } + })) + .Done(c => c.SubscriberGotIMyEvent && c.SubscriberGotMyEvent2) + .Run(); + + Assert.True(rootContext.SubscriberGotIMyEvent); + Assert.True(rootContext.SubscriberGotMyEvent2); + } + + public class Context : ScenarioContext + { + public bool SubscriberGotIMyEvent { get; set; } + public bool SubscriberGotMyEvent2 { get; set; } + public bool SubscribedToIMyEvent { get; set; } + public bool SubscribedToMyEvent2 { get; set; } + } + + public class Publisher1 : EndpointConfigurationBuilder + { + public Publisher1() + { + EndpointSetup(); + } + } + + public class Publisher2 : EndpointConfigurationBuilder + { + public Publisher2() + { + EndpointSetup(); + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher1)) + .AddMapping(typeof(Publisher2)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(IMyEvent messageThatIsEnlisted) + { + if (messageThatIsEnlisted is MyEvent2) + { + Context.SubscriberGotMyEvent2 = true; + } + else + { + Context.SubscriberGotIMyEvent = true; + } + } + } + } + + + [Serializable] + public class MyEvent1 : IMyEvent + { + } + + [Serializable] + public class MyEvent2 : IMyEvent + { + } + + public interface IMyEvent : IEvent + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event.cs new file mode 100644 index 000000000..c3f5dec98 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event.cs @@ -0,0 +1,166 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_publishing_an_event : NServiceBusAcceptanceTest + { + [Test] + public void Issue_1851() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("Subscriber3")) + { + context.Subscriber3Subscribed = true; + } + })) + .When(c => c.Subscriber3Subscribed, bus => bus.Publish()) + ) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + { + context.Subscriber3Subscribed = true; + } + })) + + .Done(c => c.Subscriber3GotTheEvent) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.Subscriber3GotTheEvent)) + .Run(); + } + + [Test] + public void Should_be_delivered_to_all_subscribers() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("Subscriber1")) + context.Subscriber1Subscribed = true; + + if (s.SubscriberReturnAddress.Queue.Contains("Subscriber2")) + context.Subscriber2Subscribed = true; + })) + .When(c => c.Subscriber1Subscribed && c.Subscriber2Subscribed, bus => bus.Publish(new MyEvent())) + ) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.Subscriber1Subscribed = true; + })) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.Subscriber2Subscribed = true; + })) + .Done(c => c.Subscriber1GotTheEvent && c.Subscriber2GotTheEvent) + .Repeat(r => r.For(Transports.Default)) + .Should(c => + { + Assert.True(c.Subscriber1GotTheEvent); + Assert.True(c.Subscriber2GotTheEvent); + }) + + .Run(); + } + + public class Context : ScenarioContext + { + public bool Subscriber1GotTheEvent { get; set; } + public bool Subscriber2GotTheEvent { get; set; } + public bool Subscriber3GotTheEvent { get; set; } + public bool Subscriber1Subscribed { get; set; } + public bool Subscriber2Subscribed { get; set; } + public bool Subscriber3Subscribed { get; set; } + } + + public class Publisher : EndpointConfigurationBuilder + { + public Publisher() + { + EndpointSetup(); + } + } + + public class Subscriber3 : EndpointConfigurationBuilder + { + public Subscriber3() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(IFoo messageThatIsEnlisted) + { + Context.Subscriber3GotTheEvent = true; + } + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber1GotTheEvent = true; + } + } + } + + public class Subscriber2 : EndpointConfigurationBuilder + { + public Subscriber2() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber2GotTheEvent = true; + } + } + } + + public interface IFoo : IEvent + { + } + + [Serializable] + public class MyEvent : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_using_a_broker_transport_with_centralized_routing.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_using_a_broker_transport_with_centralized_routing.cs new file mode 100644 index 000000000..22bcdf8e1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_using_a_broker_transport_with_centralized_routing.cs @@ -0,0 +1,94 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_publishing_an_event_using_a_broker_transport_with_centralized_routing : NServiceBusAcceptanceTest + { + [Test, Ignore] // Ignore because, test this test is unreliable. Passed on the build server without the core fix! + public void Should_be_delivered_to_allsubscribers_without_the_need_for_config() + { + Scenario.Define() + .WithEndpoint + (b => b.When(c => c.IsSubscriptionProcessedForSub1 && c.IsSubscriptionProcessedForSub2, bus => bus.Publish(new MyEvent()))) + .WithEndpoint(b => b.Given((bus, context) => + { + context.IsSubscriptionProcessedForSub1 = true; + })) + .WithEndpoint(b => b.Given((bus, context) => + { + context.IsSubscriptionProcessedForSub2 = true; + })) + .Done(c => c.Subscriber1GotTheEvent && c.Subscriber2GotTheEvent) + .Repeat(r => r.For()) + .Should(c => + { + Assert.True(c.Subscriber1GotTheEvent); + Assert.True(c.Subscriber2GotTheEvent); + }) + + .Run(); + } + + public class Context : ScenarioContext + { + public bool Subscriber1GotTheEvent { get; set; } + public bool Subscriber2GotTheEvent { get; set; } + + public bool IsSubscriptionProcessedForSub1 { get; set; } + public bool IsSubscriptionProcessedForSub2 { get; set; } + } + + public class CentralizedPublisher : EndpointConfigurationBuilder + { + public CentralizedPublisher() + { + EndpointSetup(); + } + } + + public class CentralizedSubscriber1 : EndpointConfigurationBuilder + { + public CentralizedSubscriber1() + { + EndpointSetup(); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber1GotTheEvent = true; + } + } + } + + public class CentralizedSubscriber2 : EndpointConfigurationBuilder + { + public CentralizedSubscriber2() + { + EndpointSetup(); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber2GotTheEvent = true; + } + } + } + + [Serializable] + public class MyEvent : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_only_local_messagehandlers.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_only_local_messagehandlers.cs new file mode 100644 index 000000000..bfd422d5b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_only_local_messagehandlers.cs @@ -0,0 +1,104 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_publishing_an_event_with_only_local_messagehandlers : NServiceBusAcceptanceTest + { + [Test] + public void Should_trigger_the_catch_all_handler_for_message_driven_subscriptions() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + context.LocalEndpointSubscribed = true; + })) + .When(c => c.LocalEndpointSubscribed, bus => bus.Publish(new EventHandledByLocalEndpoint())) + ) + .Done(c => c.CatchAllHandlerGotTheMessage) + .Repeat(r => r.For()) + .Should(c => Assert.True(c.CatchAllHandlerGotTheMessage)) + + .Run(); + } + + [Test] + public void Should_trigger_the_catch_all_handler_for_publishers_with_centralized_pubsub() + { + Scenario.Define() + .WithEndpoint(b => b.When(c => c.EndpointsStarted, (bus, context) => bus.Publish(new EventHandledByLocalEndpoint()))) + .Done(c => c.CatchAllHandlerGotTheMessage) + .Repeat(r => r.For(Transports.ActiveMQ)) //exclude active since the support for polymorphic routing is not implemented + .Should(c => Assert.True(c.CatchAllHandlerGotTheMessage)) + + .Run(); + } + + public class Context : ScenarioContext + { + + public bool CatchAllHandlerGotTheMessage { get; set; } + + public bool LocalEndpointSubscribed { get; set; } + } + + public class MessageDrivenPublisher : EndpointConfigurationBuilder + { + public MessageDrivenPublisher() + { + EndpointSetup() + .AddMapping(typeof(MessageDrivenPublisher)); //an explicit mapping is needed + } + + class CatchAllHandler:IHandleMessages //not enough for auto subscribe to work + { + public Context Context { get; set; } + public void Handle(IEvent message) + { + Context.CatchAllHandlerGotTheMessage = true; + } + } + + class DummyHandler : IHandleMessages //explicit handler for the event is needed + { + public Context Context { get; set; } + public void Handle(EventHandledByLocalEndpoint message) + { + } + } + } + + public class CentralizedStoragePublisher : EndpointConfigurationBuilder + { + public CentralizedStoragePublisher() + { + EndpointSetup(c => Configure.Features.AutoSubscribe(s => s.DoNotRequireExplicitRouting())); + } + + class CatchAllHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(IEvent message) + { + Context.CatchAllHandlerGotTheMessage = true; + } + } + + class DummyHandler : IHandleMessages //explicit handler for the event is needed + { + public Context Context { get; set; } + public void Handle(EventHandledByLocalEndpoint message) + { + } + } + } + [Serializable] + public class EventHandledByLocalEndpoint : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_overridden_local_address.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_overridden_local_address.cs new file mode 100644 index 000000000..f08ba1e61 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_overridden_local_address.cs @@ -0,0 +1,85 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_publishing_an_event_with_overridden_local_address : NServiceBusAcceptanceTest + { + [Test, Explicit("This test fails against RabbitMQ")] + public void Should_be_delivered_to_all_subscribers() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => + Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("myinputqueue")) + context.Subscriber1Subscribed = true; + })) + .When(c => c.Subscriber1Subscribed, bus => bus.Publish(new MyEvent())) + ) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.Subscriber1Subscribed = true; + })) + .Done(c => c.Subscriber1GotTheEvent) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.Subscriber1GotTheEvent)) + + .Run(); + } + + public class Context : ScenarioContext + { + public bool Subscriber1GotTheEvent { get; set; } + public bool Subscriber1Subscribed { get; set; } + } + + public class Publisher : EndpointConfigurationBuilder + { + public Publisher() + { + EndpointSetup(); + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class OverrideLocalAddress : IWantToRunBeforeConfiguration + { + public void Init() + { + Address.InitializeLocalAddress("myinputqueue"); + } + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber1GotTheEvent = true; + } + } + } + + [Serializable] + public class MyEvent : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_the_subscriber_scaled_out.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_the_subscriber_scaled_out.cs new file mode 100644 index 000000000..533a3e69e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_publishing_an_event_with_the_subscriber_scaled_out.cs @@ -0,0 +1,119 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using System.Collections.Generic; + using System.Linq; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using ScenarioDescriptors; + using Unicast.Subscriptions; + using Unicast.Subscriptions.MessageDrivenSubscriptions; + + public class When_publishing_an_event_with_the_subscriber_scaled_out : NServiceBusAcceptanceTest + { + static string Server1 = "Server1"; + static string Server2 = "Server2"; + + [Test]//https://github.com/NServiceBus/NServiceBus/issues/1101 + public void Should_only_publish_one_event() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue != "MyEndpoint") + return; + + context.NumberOfSubscriptionsReceived++; + })) + .When(c => c.NumberOfSubscriptionsReceived >= 2, (bus, c) => + { + c.SubscribersOfTheEvent = Configure.Instance.Builder.Build() + .GetSubscriberAddressesForMessage(new[] { new MessageType(typeof(MyEvent)) }).Select(a => a.ToString()).ToList(); + }) + ) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.NumberOfSubscriptionsReceived++; + })) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.NumberOfSubscriptionsReceived++; + })) + .Done(c => c.SubscribersOfTheEvent != null) + .Repeat(r => r.For(Transports.Msmq) + .For(SubscriptionStorages.Msmq)) + .Should(c => Assert.AreEqual(1, c.SubscribersOfTheEvent.Count(), "There should only be one logical subscriber")) + .MaxTestParallelism(1)//we force the endpoint names so we can't run this is parallel + .Run(); + } + + public class Context : ScenarioContext + { + public int NumberOfSubscriptionsReceived { get; set; } + + public IEnumerable SubscribersOfTheEvent { get; set; } + } + + public class Publisher : EndpointConfigurationBuilder + { + public Publisher() + { + EndpointSetup(); + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof (Publisher)) + .CustomMachineName(Server1) + .CustomEndpointName("MyEndpoint"); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + } + } + } + + public class Subscriber2 : EndpointConfigurationBuilder + { + public Subscriber2() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)) + .CustomMachineName(Server2) + .CustomEndpointName("MyEndpoint"); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + } + } + } + + [Serializable] + public class MyEvent : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_base_event_from_different_publishers.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_base_event_from_different_publishers.cs new file mode 100644 index 000000000..65cd2e492 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_base_event_from_different_publishers.cs @@ -0,0 +1,113 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + + public class When_subscribing_to_a_base_event_from_different_publishers : NServiceBusAcceptanceTest + { + [Test] + public void should_receive_events_from_all_publishers() + { + var cc = new Context(); + + Scenario.Define(cc) + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("Subscriber1")) + context.SubscribedToPublisher1 = true; + })) + .When(c => c.SubscribedToPublisher1, bus => bus.Publish(new DerivedEvent1())) + ) + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("Subscriber1")) + context.SubscribedToPublisher2 = true; + })) + .When(c => c.SubscribedToPublisher2, bus => bus.Publish(new DerivedEvent2())) + ) + .WithEndpoint(b => b.Given((bus, context) => + { + if (!Feature.IsEnabled()) + { + context.SubscribedToPublisher1 = true; + context.SubscribedToPublisher2 = true; + } + })) + .Done(c => c.GotTheEventFromPublisher1 && c.GotTheEventFromPublisher2) + .Run(); + + Assert.True(cc.GotTheEventFromPublisher1); + Assert.True(cc.GotTheEventFromPublisher2); + } + + public class Context : ScenarioContext + { + public bool GotTheEventFromPublisher1 { get; set; } + public bool GotTheEventFromPublisher2 { get; set; } + public bool SubscribedToPublisher1 { get; set; } + public bool SubscribedToPublisher2 { get; set; } + + } + + public class Publisher1 : EndpointConfigurationBuilder + { + public Publisher1() + { + EndpointSetup(); + } + } + + public class Publisher2 : EndpointConfigurationBuilder + { + public Publisher2() + { + EndpointSetup(); + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c => Configure.Features.Enable()) + .AddMapping(typeof(Publisher1)) + .AddMapping(typeof(Publisher2)); + } + + public class BaseEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(BaseEvent message) + { + if (message.GetType().FullName.Contains("DerivedEvent1")) + Context.GotTheEventFromPublisher1 = true; + if (message.GetType().FullName.Contains("DerivedEvent2")) + Context.GotTheEventFromPublisher2 = true; + } + } + } + + [Serializable] + public class BaseEvent : IEvent + { + } + + [Serializable] + public class DerivedEvent1 : BaseEvent + { + + } + + [Serializable] + public class DerivedEvent2 : BaseEvent + { + + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_polymorphic_event.cs b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_polymorphic_event.cs new file mode 100644 index 000000000..2d9cf6f54 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/PubSub/When_subscribing_to_a_polymorphic_event.cs @@ -0,0 +1,124 @@ +namespace NServiceBus.AcceptanceTests.PubSub +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using Unicast.Subscriptions.MessageDrivenSubscriptions; + + public class When_subscribing_to_a_polymorphic_event : NServiceBusAcceptanceTest + { + [Test] + public void Event_should_be_delivered() + { + var cc = new Context(); + + Scenario.Define(cc) + .WithEndpoint(b => b.Given((bus, context) => EnableNotificationsOnSubscribe(context)) + .When(c => c.Subscriber1Subscribed && c.Subscriber2Subscribed, bus => bus.Publish(new MyEvent()))) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.Subscriber1Subscribed = true; + })) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.Subscriber2Subscribed = true; + })) + .Done(c => c.Subscriber1GotTheEvent && c.Subscriber2GotTheEvent) + .Run(); + + Assert.True(cc.Subscriber1GotTheEvent); + Assert.True(cc.Subscriber2GotTheEvent); + } + + public class Context : ScenarioContext + { + public bool Subscriber1GotTheEvent { get; set; } + + public bool Subscriber2GotTheEvent { get; set; } + + public int NumberOfSubscribers { get; set; } + + public bool Subscriber1Subscribed { get; set; } + + public bool Subscriber2Subscribed { get; set; } + } + + public class Publisher : EndpointConfigurationBuilder + { + public Publisher() + { + EndpointSetup(); + } + } + + public class Subscriber1 : EndpointConfigurationBuilder + { + public Subscriber1() + { + EndpointSetup(c=>Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(IMyEvent messageThatIsEnlisted) + { + Context.Subscriber1GotTheEvent = true; + } + } + } + + public class Subscriber2 : EndpointConfigurationBuilder + { + public Subscriber2() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(Publisher)); + } + + public class MyEventHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyEvent messageThatIsEnlisted) + { + Context.Subscriber2GotTheEvent = true; + } + } + } + static void EnableNotificationsOnSubscribe(Context context) + { + if (Feature.IsEnabled()) + { + Configure.Instance.Builder.Build().ClientSubscribed += + (sender, args) => + { + if (args.SubscriberReturnAddress.Queue.Contains("Subscriber1")) + context.Subscriber1Subscribed = true; + + if (args.SubscriberReturnAddress.Queue.Contains("Subscriber2")) + context.Subscriber2Subscribed = true; + }; + } + } + + [Serializable] + public class MyEvent : IMyEvent + { + } + + public interface IMyEvent : IEvent + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_doing_flr_with_default_settings.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_doing_flr_with_default_settings.cs new file mode 100644 index 000000000..d899fc02b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_doing_flr_with_default_settings.cs @@ -0,0 +1,135 @@ +namespace NServiceBus.AcceptanceTests.Retries +{ + using System; + using Config; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_doing_flr_with_default_settings : NServiceBusAcceptanceTest + { + public static Func X = () => 5; + + [Test] + public void Should_do_X_retries_by_default_with_dtc_on() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MessageToBeRetried{ Id = context.Id }))) + .Done(c => c.HandedOverToSlr || c.NumberOfTimesInvoked > X()) + .Repeat(r => r.For()) + .Should(c => Assert.AreEqual(X(), c.NumberOfTimesInvoked, string.Format("The FLR should by default retry {0} times", X()))) + .Run(); + + } + + [Test] + public void Should_do_X_retries_by_default_with_native_transactions() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => + { + b.CustomConfig(c => Configure.Transactions.Advanced(a => a.DisableDistributedTransactions())); + b.Given((bus, context) => bus.SendLocal(new MessageToBeRetried { Id = context.Id })); + }) + .Done(c => c.HandedOverToSlr || c.NumberOfTimesInvoked > X()) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.AreEqual(X(), c.NumberOfTimesInvoked, string.Format("The FLR should by default retry {0} times", X()))) + .Run(TimeSpan.FromMinutes(X())); + + } + + [Test] + public void Should_not_do_any_retries_if_transactions_are_off() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => + { + b.CustomConfig(c => Configure.Transactions.Disable()); + b.Given((bus, context) => + { + bus.SendLocal(new MessageToBeRetried { Id = context.Id }); + bus.SendLocal(new MessageToBeRetried { Id = context.Id, SecondMessage = true }); + }); + }) + .Done(c => c.SecondMessageReceived || c.NumberOfTimesInvoked > 1) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.AreEqual(1, c.NumberOfTimesInvoked, "No retries should be in use if transactions are off")) + .Run(); + + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + + public int NumberOfTimesInvoked { get; set; } + + public bool HandedOverToSlr { get; set; } + + public bool SecondMessageReceived { get; set; } + } + + public class RetryEndpoint : EndpointConfigurationBuilder + { + public RetryEndpoint() + { + EndpointSetup( + c => c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance)) + .WithConfig(c => c.MaximumConcurrencyLevel = 1) + .AllowExceptions(); + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.HandedOverToSlr = true; + } + + public void Init(Address address) + { + + } + } + + class MessageToBeRetriedHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageToBeRetried message) + { + if (message.Id != Context.Id) return; // messages from previous test runs must be ignored + + if (message.SecondMessage) + { + Context.SecondMessageReceived = true; + return; + } + + Context.NumberOfTimesInvoked++; + + throw new Exception("Simulated exception"); + } + } + } + + [Serializable] + public class MessageToBeRetried : IMessage + { + public Guid Id { get; set; } + + public bool SecondMessage { get; set; } + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_message_fails_with_retries_set_to_0.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_message_fails_with_retries_set_to_0.cs new file mode 100644 index 000000000..5441bb94c --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_message_fails_with_retries_set_to_0.cs @@ -0,0 +1,87 @@ +namespace NServiceBus.AcceptanceTests.Retries +{ + using System; + using System.Collections.Generic; + using Config; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_message_fails_with_retries_set_to_0 : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_retry_the_message_using_flr() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeRetried()))) + .Done(c => c.HandedOverToSlr) + .Run(); + + Assert.AreEqual(1, context.NumberOfTimesInvoked,"No FLR should be in use if MaxRetries is set to 0"); + Assert.True(context.HeadersOfTheFailedMessage[Headers.ProcessingEndpoint].Contains("RetryEndpoint"), "The receiver should attach its endpoint name as a header"); + } + + public class Context : ScenarioContext + { + public int NumberOfTimesInvoked { get; set; } + + public bool HandedOverToSlr { get; set; } + + public Dictionary HeadersOfTheFailedMessage { get; set; } + } + + public class RetryEndpoint : EndpointConfigurationBuilder + { + public RetryEndpoint() + { + EndpointSetup(c => c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance)) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class CustomFaultManager: IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.HandedOverToSlr = true; + Context.HeadersOfTheFailedMessage = message.Headers; + } + + public void Init(Address address) + { + + } + } + + class MessageToBeRetriedHandler:IHandleMessages + { + public Context Context { get; set; } + public void Handle(MessageToBeRetried message) + { + Context.NumberOfTimesInvoked++; + throw new Exception("Simulated exception"); + } + } + } + + [Serializable] + public class MessageToBeRetried : IMessage + { + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_messages_fails_flr.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_messages_fails_flr.cs new file mode 100644 index 000000000..587df1e1f --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_messages_fails_flr.cs @@ -0,0 +1,94 @@ +namespace NServiceBus.AcceptanceTests.Retries +{ + using System; + using Config; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_messages_fails_flr : NServiceBusAcceptanceTest + { + static TimeSpan SlrDelay = TimeSpan.FromSeconds(5); + + [Test] + public void Should_be_moved_to_slr() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MessageToBeRetried { Id = context.Id }))) + .Done(c => c.NumberOfTimesInvoked >= 2) + .Repeat(r => r.For(Transports.Default)) + .Should(context => + { + Assert.GreaterOrEqual(1,context.NumberOfSlrRetriesPerformed, "The SLR should only do one retry"); + Assert.GreaterOrEqual(context.TimeOfSecondAttempt - context.TimeOfFirstAttempt,SlrDelay , "The SLR should delay the retry"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + + public int NumberOfTimesInvoked { get; set; } + + public DateTime TimeOfFirstAttempt { get; set; } + public DateTime TimeOfSecondAttempt { get; set; } + + public int NumberOfSlrRetriesPerformed { get; set; } + } + + public class SLREndpoint : EndpointConfigurationBuilder + { + public SLREndpoint() + { + EndpointSetup() + .AllowExceptions() + .WithConfig(c => + { + c.MaxRetries = 0; //to skip the FLR + }) + .WithConfig(c => + { + c.NumberOfRetries = 1; + c.TimeIncrease = SlrDelay; + }); + } + + + class MessageToBeRetriedHandler:IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MessageToBeRetried message) + { + if (message.Id != Context.Id) return; // ignore messages from previous test runs + + Context.NumberOfTimesInvoked++; + + if (Context.NumberOfTimesInvoked == 1) + Context.TimeOfFirstAttempt = DateTime.UtcNow; + + if (Context.NumberOfTimesInvoked == 2) + { + Context.TimeOfSecondAttempt = DateTime.UtcNow; + } + + Context.NumberOfSlrRetriesPerformed = int.Parse(Bus.CurrentMessageContext.Headers[Headers.Retries]); + + throw new Exception("Simulated exception"); + } + } + } + + [Serializable] + public class MessageToBeRetried : IMessage + { + public Guid Id { get; set; } + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_sending_a_message_off_to_slr.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_sending_a_message_off_to_slr.cs new file mode 100644 index 000000000..4f1a8c6fd --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Retries/When_sending_a_message_off_to_slr.cs @@ -0,0 +1,199 @@ +namespace NServiceBus.AcceptanceTests.Retries +{ + using System; + using System.Linq; + using Config; + using Faults; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NServiceBus.Unicast; + using NServiceBus.Unicast.Transport; + using NUnit.Framework; + +#pragma warning disable 612, 618 + + public class When_sending_a_message_off_to_slr : NServiceBusAcceptanceTest + { + [Test] + public void Should_preserve_the_original_body_for_regular_exceptions() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeRetried()))) + .Done(c => c.SlrChecksum != default(byte)) + .Run(); + + Assert.AreEqual(context.OriginalBodyChecksum, context.SlrChecksum, "The body of the message sent to slr should be the same as the original message coming off the queue"); + + } + + [Test] + public void Should_raise_FinishedMessageProcessing_event() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeRetried()))) + .Done(c => c.FinishedMessageProcessingCalledAfterFaultManagerInvoked) + .Run(); + + Assert.IsTrue(context.FinishedMessageProcessingCalledAfterFaultManagerInvoked); + + } + + [Test] + public void Should_preserve_the_original_body_for_serialization_exceptions() + { + var context = new Context + { + SimulateSerializationException = true + }; + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MessageToBeRetried()))) + .Done(c => c.SlrChecksum != default(byte)) + .Run(); + + Assert.AreEqual(context.OriginalBodyChecksum, context.SlrChecksum, "The body of the message sent to slr should be the same as the original message coming off the queue"); + + } + + public class Context : ScenarioContext + { + public bool FinishedMessageProcessingCalledAfterFaultManagerInvoked { get; set; } + public bool FaultManagerInvoked { get; set; } + + public byte OriginalBodyChecksum { get; set; } + + public byte SlrChecksum { get; set; } + + public bool SimulateSerializationException { get; set; } + } + + public class RetryEndpoint : EndpointConfigurationBuilder + { + public RetryEndpoint() + { + EndpointSetup(c => + { + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + c.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + }) + .WithConfig(c => + { + c.MaxRetries = 0; + }) + .AllowExceptions(); + } + + class FinishedProcessingListener : IWantToRunWhenBusStartsAndStops + { + readonly Context context; + + public FinishedProcessingListener(UnicastBus bus, Context context) + { + this.context = context; + bus.Transport.FinishedMessageProcessing += Transport_FinishedMessageProcessing; + } + + void Transport_FinishedMessageProcessing(object sender, FinishedMessageProcessingEventArgs e) + { + if (context.FaultManagerInvoked) + { + context.FinishedMessageProcessingCalledAfterFaultManagerInvoked = true; + } + } + + public void Start() + { + } + + public void Stop() + { + } + } + + class BodyMutator : IMutateTransportMessages, NServiceBus.INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + + var originalBody = transportMessage.Body; + + Context.OriginalBodyChecksum = Checksum(originalBody); + + var decryptedBody = new byte[originalBody.Length]; + + Buffer.BlockCopy(originalBody,0,decryptedBody,0,originalBody.Length); + + //decrypt + decryptedBody[0]++; + + if (Context.SimulateSerializationException) + decryptedBody[1]++; + + transportMessage.Body = decryptedBody; + } + + + public void MutateOutgoing(object[] messages, TransportMessage transportMessage) + { + transportMessage.Body[0]--; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + class CustomFaultManager : IManageMessageFailures + { + public Context Context { get; set; } + + public void SerializationFailedForMessage(TransportMessage message, Exception e) + { + Context.FaultManagerInvoked = true; + Context.SlrChecksum = Checksum(message.Body); + } + + public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e) + { + Context.FaultManagerInvoked = true; + Context.SlrChecksum = Checksum(message.Body); + } + + public void Init(Address address) + { + + } + } + + class MessageToBeRetriedHandler : IHandleMessages + { + public void Handle(MessageToBeRetried message) + { + throw new Exception("Simulated exception"); + } + } + + public static byte Checksum(byte[] data) + { + var longSum = data.Sum(x => (long)x); + return unchecked((byte)longSum); + } + } + + [Serializable] + public class MessageToBeRetried : IMessage + { + } + } + +#pragma warning restore 612, 618 + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_1819.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_1819.cs new file mode 100644 index 000000000..fc4a344bc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_1819.cs @@ -0,0 +1,118 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + + public class Issue_1819 : NServiceBusAcceptanceTest + { + [Test] + public void Run() + { + var context = new Context { Id = Guid.NewGuid() }; + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new StartSaga1 { ContextId = c.Id }))) + .Done(c => (c.Saga1TimeoutFired && c.Saga2TimeoutFired) || c.SagaNotFound) + .Run(TimeSpan.FromSeconds(20)); + + Assert.IsFalse(context.SagaNotFound); + Assert.IsTrue(context.Saga1TimeoutFired); + Assert.IsTrue(context.Saga2TimeoutFired); + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + public bool Saga1TimeoutFired { get; set; } + public bool Saga2TimeoutFired { get; set; } + public bool SagaNotFound { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleTimeouts, IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(StartSaga1 message) + { + if (message.ContextId != Context.Id) return; + + RequestTimeout(TimeSpan.FromSeconds(5), new Saga1Timeout { ContextId = Context.Id }); + RequestTimeout(new DateTime(2011, 10, 14, 23, 08, 0, DateTimeKind.Local), new Saga2Timeout { ContextId = Context.Id }); + } + + public void Timeout(Saga1Timeout state) + { + MarkAsComplete(); + + if (state.ContextId != Context.Id) return; + Context.Saga1TimeoutFired = true; + } + + public void Timeout(Saga2Timeout state) + { + if (state.ContextId != Context.Id) return; + Context.Saga2TimeoutFired = true; + } + + public class Saga1Data : ContainSagaData + { + } + } + + public class SagaNotFound : IHandleSagaNotFound + { + public Context Context { get; set; } + + public void Handle(object message) + { + if (((dynamic)message).ContextId != Context.Id) return; + + Context.SagaNotFound = true; + } + } + + public class CatchAllMessageHandler : IHandleMessages + { + public void Handle(object message) + { + + } + } + + public class Foo : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.SpecifyFirst(); + } + } + } + + [Serializable] + public class StartSaga1 : ICommand + { + public Guid ContextId { get; set; } + } + + + public class Saga1Timeout + { + public Guid ContextId { get; set; } + } + + public class Saga2Timeout + { + public Guid ContextId { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_2044.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_2044.cs new file mode 100644 index 000000000..cbd0e0c53 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/Issue_2044.cs @@ -0,0 +1,99 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + + public class Issue_2044 : NServiceBusAcceptanceTest + { + [Test] + public void Run() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Send(new MessageToSaga()))) + .WithEndpoint() + .Done(c => c.ReplyReceived) + .Run(); + + Assert.IsTrue(context.ReplyReceived); + } + + public class Context : ScenarioContext + { + public bool ReplyReceived { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup() + .AddMapping(typeof(ReceiverWithSaga)); + } + + public class ReplyHandler : IHandleMessages + { + public Context Context { get; set; } + + + public void Handle(Reply message) + { + Context.ReplyReceived = true; + } + } + } + + public class ReceiverWithSaga : EndpointConfigurationBuilder + { + public ReceiverWithSaga() + { + EndpointSetup(); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleMessages + { + + public void Handle(StartSaga1 message) + { + } + + public void Handle(MessageToSaga message) + { + } + + public class Saga1Data : ContainSagaData + { + } + } + + public class SagaNotFound : IHandleSagaNotFound + { + public IBus Bus { get; set; } + + public void Handle(object message) + { + Bus.Reply(new Reply()); + } + } + } + + [Serializable] + public class StartSaga1 : ICommand + { + } + + [Serializable] + public class MessageToSaga : ICommand + { + } + + [Serializable] + public class Reply : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_is_started_by_an_event_published_by_another_saga.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_is_started_by_an_event_published_by_another_saga.cs new file mode 100644 index 000000000..e218e3ac5 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_is_started_by_an_event_published_by_another_saga.cs @@ -0,0 +1,217 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using PubSub; + using Saga; + using ScenarioDescriptors; + + //Repro for #1323 + public class When_a_saga_is_started_by_an_event_published_by_another_saga : NServiceBusAcceptanceTest + { + [Test] + public void Should_start_the_saga_and_request_a_timeout() + { + Scenario.Define() + .WithEndpoint(b => + b.Given( + (bus, context) => + Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("SagaThatIsStartedByTheEvent")) + { + context.IsEventSubscriptionReceived = true; + } + })) + .When(c => c.IsEventSubscriptionReceived, + bus => + bus.SendLocal(new StartSaga {DataId = Guid.NewGuid()})) + ) + .WithEndpoint( + b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.IsEventSubscriptionReceived = true; + })) + + .Done(c => c.DidSaga1Complete && c.DidSaga2Complete) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSaga1Complete && c.DidSaga2Complete)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool DidSaga1Complete { get; set; } + public bool DidSaga2Complete { get; set; } + public bool IsEventSubscriptionReceived { get; set; } + } + + + [Test] + [Ignore("Not stable")] + public void Should_start_the_saga_when_set_up_to_start_for_the_base_event() + { + Scenario.Define() + .WithEndpoint(b => + b.Given( + (bus, context) => + Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("SagaThatIsStartedByABaseEvent")) + { + context.IsEventSubscriptionReceived = true; + } + })) + .When(c => c.IsEventSubscriptionReceived, + bus => + bus.Publish(m=> { m.DataId = Guid.NewGuid(); })) + ) + .WithEndpoint( + b => b.Given((bus, context) => + { + bus.Subscribe(); + + if (!Feature.IsEnabled()) + context.IsEventSubscriptionReceived = true; + })) + .Done(c => c.DidSagaComplete) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSagaComplete)) + .Run(TimeSpan.FromMinutes(3)); + } + + public class SagaContext : ScenarioContext + { + public bool IsEventSubscriptionReceived { get; set; } + public bool DidSagaComplete { get; set; } + } + + public class SagaThatPublishesAnEvent : EndpointConfigurationBuilder + { + public SagaThatPublishesAnEvent() + { + EndpointSetup(c => Configure.Features.Disable()); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(StartSaga message) + { + Data.DataId = message.DataId; + + //Publish the event, which will start the second saga + Bus.Publish(m => { m.DataId = message.DataId; }); + + //Request a timeout + RequestTimeout(TimeSpan.FromSeconds(5)); + } + + public void Timeout(Timeout1 state) + { + MarkAsComplete(); + Context.DidSaga1Complete = true; + } + + public class Saga1Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + + public class Timeout1 + { + } + } + } + + public class SagaThatIsStartedByTheEvent : EndpointConfigurationBuilder + { + public SagaThatIsStartedByTheEvent() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(SagaThatPublishesAnEvent)); + + } + + public class Saga2 : Saga, IAmStartedByMessages, IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(SomethingHappenedEvent message) + { + Data.DataId = message.DataId; + + //Request a timeout + RequestTimeout(TimeSpan.FromSeconds(5)); + } + + public void Timeout(Saga2Timeout state) + { + MarkAsComplete(); + Context.DidSaga2Complete = true; + } + + public class Saga2Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + + public class Saga2Timeout + { + } + } + } + + public class SagaThatIsStartedByABaseEvent : EndpointConfigurationBuilder + { + public SagaThatIsStartedByABaseEvent() + { + EndpointSetup(c => Configure.Features.Disable()) + .AddMapping(typeof(SagaThatPublishesAnEvent)); + } + + public class SagaStartedByBaseEvent : Saga, IAmStartedByMessages + { + public SagaContext Context { get; set; } + + public void Handle(BaseEvent message) + { + Data.DataId = message.DataId; + MarkAsComplete(); + Context.DidSagaComplete = true; + } + + public class SagaData : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + } + } + + [Serializable] + public class StartSaga : ICommand + { + public Guid DataId { get; set; } + } + + public interface SomethingHappenedEvent : BaseEvent + { + + } + + public interface BaseEvent : IEvent + { + Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_message_goes_through_the_slr.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_message_goes_through_the_slr.cs new file mode 100644 index 000000000..bc98abed8 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_a_saga_message_goes_through_the_slr.cs @@ -0,0 +1,97 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + //repro for issue: https://github.com/NServiceBus/NServiceBus/issues/1020 + public class When_a_saga_message_goes_through_the_slr : NServiceBusAcceptanceTest + { + [Test] + public void Should_invoke_the_correct_handle_methods_on_the_saga() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSagaMessage { SomeId = Guid.NewGuid() }))) + .Done(c => c.SecondMessageProcessed) + .Repeat(r => r.For(Transports.Default)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool SecondMessageProcessed { get; set; } + + + public int NumberOfTimesInvoked { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup() + .AllowExceptions(); + } + + public class TestSaga : Saga, IAmStartedByMessages,IHandleMessages + { + public Context Context { get; set; } + public void Handle(StartSagaMessage message) + { + Data.SomeId = message.SomeId; + + Bus.SendLocal(new SecondSagaMessage + { + SomeId = Data.SomeId + }); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m=>m.SomeId) + .ToSaga(s=>s.SomeId); + ConfigureMapping(m => m.SomeId) + .ToSaga(s => s.SomeId); + } + + public void Handle(SecondSagaMessage message) + { + Context.NumberOfTimesInvoked++; + var shouldFail = Context.NumberOfTimesInvoked < 2; //1 FLR and 1 SLR + + if(shouldFail) + throw new Exception("Simulated exception"); + + Context.SecondMessageProcessed = true; + } + } + + public class TestSagaData : IContainSagaData + { + public virtual Guid Id { get; set; } + public virtual string Originator { get; set; } + public virtual string OriginalMessageId { get; set; } + public virtual Guid SomeId { get; set; } + } + } + + [Serializable] + public class StartSagaMessage : ICommand + { + public Guid SomeId { get; set; } + } + public class SecondSagaMessage : ICommand + { + public Guid SomeId { get; set; } + } + + public class SomeTimeout + { + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_an_endpoint_replies_to_a_saga.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_an_endpoint_replies_to_a_saga.cs new file mode 100644 index 000000000..8a56e0b1f --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_an_endpoint_replies_to_a_saga.cs @@ -0,0 +1,108 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + // Repro for issue https://github.com/NServiceBus/NServiceBus/issues/1277 to test the fix + // making sure that the saga correlation still works. + public class When_an_endpoint_replies_to_a_saga : NServiceBusAcceptanceTest + { + [Test] + public void Should_correlate_all_saga_messages_properly() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSaga { DataId = Guid.NewGuid() }))) + .WithEndpoint() + .Done(c => c.DidSagaReplyMessageGetCorrelated) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSagaReplyMessageGetCorrelated)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool DidSagaReplyMessageGetCorrelated { get; set; } + } + + public class EndpointThatHandlesAMessageFromSagaAndReplies : EndpointConfigurationBuilder + { + public EndpointThatHandlesAMessageFromSagaAndReplies() + { + EndpointSetup(); + } + + class DoSomethingHandler : IHandleMessages + { + public IBus Bus { get; set; } + + public void Handle(DoSomething message) + { + Console.WriteLine("Received DoSomething command for DataId:{0} ... and responding with a reply", message.DataId); + Bus.Reply(new DoSomethingResponse { DataId = message.DataId }); + } + } + } + + public class EndpointThatHostsASaga : EndpointConfigurationBuilder + { + public EndpointThatHostsASaga() + { + EndpointSetup() + .AddMapping(typeof (EndpointThatHandlesAMessageFromSagaAndReplies)); + + } + + public class Saga2 : Saga, IAmStartedByMessages, IHandleMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga message) + { + var dataId = Guid.NewGuid(); + Console.Out.WriteLine("Saga2 sending DoSomething for DataId: {0}", dataId); + Data.DataId = dataId; + Bus.Send(new DoSomething { DataId = dataId }); + } + + public void Handle(DoSomethingResponse message) + { + Context.DidSagaReplyMessageGetCorrelated = message.DataId == Data.DataId; + Console.Out.WriteLine("Saga received DoSomethingResponse for DataId: {0} and MarkAsComplete", message.DataId); + MarkAsComplete(); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + + public class MySaga2Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + } + } + + + [Serializable] + public class StartSaga : ICommand + { + public Guid DataId { get; set; } + } + + public class DoSomething : ICommand + { + public Guid DataId { get; set; } + } + + public class DoSomethingResponse : IMessage + { + public Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_doing_request_response_between_sagas.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_doing_request_response_between_sagas.cs new file mode 100644 index 000000000..ec73b35c1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_doing_request_response_between_sagas.cs @@ -0,0 +1,104 @@ + +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_doing_request_response_between_sagas : NServiceBusAcceptanceTest + { + [Test] + public void Should_autocorrelate_the_response_back_to_the_requesting_saga() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new InitiateRequestingSaga { DataId = Guid.NewGuid() }))) + .Done(c => c.DidRequestingSagaGetTheResponse) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidRequestingSagaGetTheResponse)) + .Run(TimeSpan.FromSeconds(20)); + } + + public class Context : ScenarioContext + { + public bool DidRequestingSagaGetTheResponse { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + + public Endpoint() + { + EndpointSetup(); + } + + public class RequestingSaga : Saga, + IAmStartedByMessages, + IHandleMessages + { + public Context Context { get; set; } + + public void Handle(InitiateRequestingSaga message) + { + Data.DataId = message.DataId; + Bus.SendLocal(new RequestToRespondingSaga { DataId = message.DataId }); + } + + public void Handle(ResponseFromOtherSaga message) + { + Context.DidRequestingSagaGetTheResponse = true; + MarkAsComplete(); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + public class RequestingSagaData : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + } + + public class RespondingSaga : Saga, + IAmStartedByMessages + { + public Context Context { get; set; } + + public void Handle(RequestToRespondingSaga message) + { + Bus.Reply(new ResponseFromOtherSaga { DataId = message.DataId }); + } + + public override void ConfigureHowToFindSaga() + { + } + + public class RespondingSagaData : ContainSagaData + { + } + } + } + + [Serializable] + public class InitiateRequestingSaga : ICommand + { + public Guid DataId { get; set; } + } + + [Serializable] + public class RequestToRespondingSaga : ICommand + { + public Guid DataId { get; set; } + } + + [Serializable] + public class ResponseFromOtherSaga : IMessage + { + public Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_message_has_a_saga_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_message_has_a_saga_id.cs new file mode 100644 index 000000000..862c205db --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_message_has_a_saga_id.cs @@ -0,0 +1,95 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Saga; + using NUnit.Framework; + + public class When_message_has_a_saga_id : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_start_a_new_saga_if_not_found() + { + var context = Scenario.Define() + .WithEndpoint(b => b.Given(bus => + { + var message = new MessageWithSagaId(); + + bus.SetMessageHeader(message, Headers.SagaId, Guid.NewGuid().ToString()); + bus.SetMessageHeader(message, Headers.SagaType, typeof(MySaga).AssemblyQualifiedName); + + bus.SendLocal(message); + })) + .Done(c => c.NotFoundHandlerCalled && c.OtherSagaStarted) + .Run(TimeSpan.FromSeconds(15)); + + Assert.True(context.NotFoundHandlerCalled); + Assert.True(context.OtherSagaStarted); + Assert.False(context.MessageHandlerCalled); + Assert.False(context.TimeoutHandlerCalled); + } + + class MySaga : Saga, IAmStartedByMessages, + IHandleTimeouts, + IHandleSagaNotFound + { + public Context Context { get; set; } + + public class SagaData : ContainSagaData + { + } + + public void Handle(MessageWithSagaId message) + { + Context.MessageHandlerCalled = true; + } + + public void Handle(object message) + { + Context.NotFoundHandlerCalled = true; + } + + public void Timeout(MessageWithSagaId state) + { + Context.TimeoutHandlerCalled = true; + } + } + + class MyOtherSaga : Saga, IAmStartedByMessages + { + public Context Context { get; set; } + + public void Handle(MessageWithSagaId message) + { + Context.OtherSagaStarted = true; + } + + public class SagaData : ContainSagaData + { + } + + } + + + class Context : ScenarioContext + { + public bool NotFoundHandlerCalled { get; set; } + public bool MessageHandlerCalled { get; set; } + public bool TimeoutHandlerCalled { get; set; } + public bool OtherSagaStarted { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup(); + } + } + + public class MessageWithSagaId : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_is_mapped_to_an_existing_saga_instance.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_is_mapped_to_an_existing_saga_instance.cs new file mode 100644 index 000000000..619f3d54d --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_is_mapped_to_an_existing_saga_instance.cs @@ -0,0 +1,89 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_receiving_a_message_that_is_mapped_to_an_existing_saga_instance : NServiceBusAcceptanceTest + { + static Guid IdThatSagaIsCorrelatedOn = Guid.NewGuid(); + + [Test] + public void Should_hydrate_and_invoke_the_existing_instance() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => + { + bus.SendLocal(new StartSagaMessage { SomeId = IdThatSagaIsCorrelatedOn }); + bus.SendLocal(new StartSagaMessage { SomeId = IdThatSagaIsCorrelatedOn, SecondMessage = true }); + })) + .Done(c => c.SecondMessageReceived) + .Repeat(r => r.For(SagaPersisters.Default)) + .Should(c => Assert.AreEqual(c.FirstSagaInstance, c.SecondSagaInstance, "The same saga instance should be invoked invoked for both messages")) + + .Run(); + } + + public class Context : ScenarioContext + { + public bool SecondMessageReceived { get; set; } + + public Guid FirstSagaInstance { get; set; } + public Guid SecondSagaInstance { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup(c=>Configure.Transactions.Advanced(a => a.DoNotWrapHandlersExecutionInATransactionScope())); + } + + public class TestSaga : Saga, IAmStartedByMessages + { + public Context Context { get; set; } + public void Handle(StartSagaMessage message) + { + Data.SomeId = message.SomeId; + + if (message.SecondMessage) + { + Context.SecondSagaInstance = Data.Id; + Context.SecondMessageReceived = true; + } + else + { + Context.FirstSagaInstance = Data.Id; + } + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m=>m.SomeId) + .ToSaga(s=>s.SomeId); + } + } + + public class TestSagaData : IContainSagaData + { + public virtual Guid Id { get; set; } + public virtual string Originator { get; set; } + public virtual string OriginalMessageId { get; set; } + + [Unique] + public virtual Guid SomeId { get; set; } + } + } + + [Serializable] + public class StartSagaMessage : ICommand + { + public Guid SomeId { get; set; } + + public bool SecondMessage { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_complete_saga.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_complete_saga.cs new file mode 100644 index 000000000..7cf2281a8 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_complete_saga.cs @@ -0,0 +1,141 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_receiving_a_message_that_completes_the_saga : NServiceBusAcceptanceTest + { + [Test] + public void Should_hydrate_and_complete_the_existing_instance() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => + { + b.Given((bus, context) => bus.SendLocal(new StartSagaMessage {SomeId = context.Id})); + b.When(context => context.StartSagaMessageReceived, (bus, context) => bus.SendLocal(new CompleteSagaMessage { SomeId = context.Id })); + }) + .Done(c => c.SagaCompleted) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.IsNull(c.UnhandledException)) + + .Run(); + } + + [Test] + public void Should_ignore_messages_afterwards() + { + Scenario.Define(() => new Context {Id = Guid.NewGuid()}) + .WithEndpoint(b => + { + b.Given((bus, context) => bus.SendLocal(new StartSagaMessage { SomeId = context.Id })); + b.When(context => context.StartSagaMessageReceived, (bus, context) => bus.SendLocal(new CompleteSagaMessage { SomeId = context.Id })); + b.When(context => context.SagaCompleted, (bus, context) => bus.SendLocal(new AnotherMessage { SomeId = context.Id })); + }) + .Done(c => c.AnotherMessageReceived) + .Repeat(r => r.For(Transports.Default)) + .Should(c => + { + Assert.IsNull(c.UnhandledException); + Assert.False(c.SagaReceivedAnotherMessage,"AnotherMessage should not be delivered to the saga after completion"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public Exception UnhandledException { get; set; } + public Guid Id { get; set; } + + public bool StartSagaMessageReceived { get; set; } + + public bool SagaCompleted { get; set; } + + public bool AnotherMessageReceived { get; set; } + public bool SagaReceivedAnotherMessage { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup( + c => c.RavenSagaPersister().UnicastBus().LoadMessageHandlers>()); + } + + + + public class TestSaga : Saga, IAmStartedByMessages, IHandleMessages, IHandleMessages + { + public Context Context { get; set; } + + public void Handle(StartSagaMessage message) + { + Data.SomeId = message.SomeId; + + Context.StartSagaMessageReceived = true; + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m=>m.SomeId) + .ToSaga(s=>s.SomeId); + ConfigureMapping(m => m.SomeId) + .ToSaga(s => s.SomeId); + ConfigureMapping(m => m.SomeId) + .ToSaga(s => s.SomeId); + } + + public void Handle(CompleteSagaMessage message) + { + MarkAsComplete(); + Context.SagaCompleted = true; + } + + public void Handle(AnotherMessage message) + { + Context.SagaReceivedAnotherMessage = true; + } + } + + public class TestSagaData : IContainSagaData + { + public virtual Guid Id { get; set; } + public virtual string Originator { get; set; } + public virtual string OriginalMessageId { get; set; } + [Unique] + public virtual Guid SomeId { get; set; } + } + } + + public class CompletionHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(AnotherMessage message) + { + Context.AnotherMessageReceived = true; + } + } + + [Serializable] + public class StartSagaMessage : ICommand + { + public Guid SomeId { get; set; } + } + + [Serializable] + public class CompleteSagaMessage : ICommand + { + public Guid SomeId { get; set; } + } + + [Serializable] + public class AnotherMessage : ICommand + { + public Guid SomeId { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_start_a_saga.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_start_a_saga.cs new file mode 100644 index 000000000..9ca704946 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_receiving_a_message_that_should_start_a_saga.cs @@ -0,0 +1,101 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_receiving_a_message_that_should_start_a_saga : NServiceBusAcceptanceTest + { + [Test] + public void Should_start_the_saga_and_call_all_messagehandlers_for_the_given_message() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSagaMessage()))) + .Done(context => context.InterceptingHandlerCalled && context.SagaStarted) + .Repeat(r => r.For()) + .Should(c => + { + Assert.True(c.InterceptingHandlerCalled, "The message handler should be called"); + Assert.True(c.SagaStarted, "The saga should have been started"); + }) + .Run(); + } + + + [Test] + public void Should_not_start_saga_if_a_interception_handler_has_been_invoked() + { + Scenario.Define(() => new SagaEndpointContext{InterceptSaga = true}) + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSagaMessage()))) + .Done(context => context.InterceptingHandlerCalled) + .Repeat(r => r.For()) + .Should(c => + { + Assert.True(c.InterceptingHandlerCalled, "The intercepting handler should be called"); + Assert.False(c.SagaStarted, "The saga should not have been started since the intercepting handler stops the pipeline"); + }) + .Run(); + } + + + public class SagaEndpointContext : ScenarioContext + { + public bool InterceptingHandlerCalled { get; set; } + + public bool SagaStarted { get; set; } + + public bool InterceptSaga { get; set; } + } + + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup(c =>c.UnicastBus().LoadMessageHandlers>()); + } + + public class TestSaga : Saga, IAmStartedByMessages + { + public SagaEndpointContext Context { get; set; } + public void Handle(StartSagaMessage message) + { + Context.SagaStarted = true; + } + } + + public class TestSagaData : IContainSagaData + { + public virtual Guid Id { get; set; } + public virtual string Originator { get; set; } + public virtual string OriginalMessageId { get; set; } + } + + public class InterceptingHandler : IHandleMessages + { + public SagaEndpointContext Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(StartSagaMessage message) + { + Context.InterceptingHandlerCalled = true; + + if(Context.InterceptSaga) + Bus.DoNotContinueDispatchingCurrentMessageToHandlers(); + } + } + } + + [Serializable] + public class StartSagaMessage : ICommand + { + } + + + } + +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_reply_from_a_finder.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_reply_from_a_finder.cs new file mode 100644 index 000000000..ef82484ef --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_reply_from_a_finder.cs @@ -0,0 +1,100 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using NServiceBus.AcceptanceTesting; + using NServiceBus.AcceptanceTests.EndpointTemplates; + using NServiceBus.Saga; + using NUnit.Framework; + + [TestFixture] + public class When_reply_from_a_finder + { + [Test] + public void Should_be_received_by_handler() + { + var context = new Context + { + Id = Guid.NewGuid() + }; + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new StartSagaMessage + { + Id = c.Id + }))) + .Done(c => c.HandlerFired) + .Run(); + + Assert.True(context.HandlerFired); + } + + public class Context : ScenarioContext + { + public bool HandlerFired { get; set; } + public Guid Id { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup(); + } + + class CustomFinder : IFindSagas.Using + { + public IBus Bus { get; set; } + public Context Context { get; set; } + + public TestSaga.SagaData FindBy(StartSagaMessage message) + { + Bus.Reply(new SagaNotFoundMessage + { + Id = Context.Id + }); + return null; + } + } + + public class TestSaga : Saga, + IAmStartedByMessages + { + public Context Context { get; set; } + + public void Handle(StartSagaMessage message) + { + } + + public class SagaData : ContainSagaData + { + } + } + + public class Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(SagaNotFoundMessage message) + { + if (Context.Id != message.Id) + { + return; + } + Context.HandlerFired = true; + } + } + } + + [Serializable] + public class SagaNotFoundMessage : IMessage + { + public Guid Id { get; set; } + } + + [Serializable] + public class StartSagaMessage : IMessage + { + public Guid Id { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_handle.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_handle.cs new file mode 100644 index 000000000..22ffe031a --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_handle.cs @@ -0,0 +1,105 @@ + +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_sending_from_a_saga_handle : NServiceBusAcceptanceTest + { + [Test] + public void Should_match_different_saga() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSaga1 { DataId = Guid.NewGuid() }))) + .Done(c => c.DidSaga2ReceiveMessage) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSaga2ReceiveMessage)) + .Run(TimeSpan.FromSeconds(20)); + } + + public class Context : ScenarioContext + { + public bool DidSaga2ReceiveMessage { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + + public Endpoint() + { + EndpointSetup(); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga1 message) + { + var dataId = Guid.NewGuid(); + Data.DataId = dataId; + Bus.SendLocal(new MessageSaga1WillHandle + { + DataId = dataId + }); + } + + public void Handle(MessageSaga1WillHandle message) + { + Bus.SendLocal(new StartSaga2()); + MarkAsComplete(); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + + } + + public class Saga1Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + + + public class Saga2 : Saga, IAmStartedByMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga2 message) + { + Context.DidSaga2ReceiveMessage = true; + } + + public class Saga2Data : ContainSagaData + { + } + } + + } + + + [Serializable] + public class StartSaga1 : ICommand + { + public Guid DataId { get; set; } + } + + + [Serializable] + public class StartSaga2 : ICommand + { + } + public class MessageSaga1WillHandle : IMessage + { + public Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_timeout.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_timeout.cs new file mode 100644 index 000000000..f10cb70a7 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_sending_from_a_saga_timeout.cs @@ -0,0 +1,88 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + public class When_sending_from_a_saga_timeout : NServiceBusAcceptanceTest + { + [Test] + public void Should_match_different_saga() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSaga1()))) + .Done(c => c.DidSaga2ReceiveMessage) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSaga2ReceiveMessage)) + .Run(TimeSpan.FromSeconds(20)); + } + + public class Context : ScenarioContext + { + public bool DidSaga2ReceiveMessage { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + + public Endpoint() + { + EndpointSetup(); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(StartSaga1 message) + { + RequestTimeout(TimeSpan.FromSeconds(1), new Saga1Timeout()); + } + + public void Timeout(Saga1Timeout state) + { + Bus.SendLocal(new StartSaga2()); + MarkAsComplete(); + } + public class Saga1Data : ContainSagaData + { + } + } + + + + public class Saga2 : Saga, IAmStartedByMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga2 message) + { + Context.DidSaga2ReceiveMessage = true; + } + + public class Saga2Data : ContainSagaData + { + } + } + + } + + + [Serializable] + public class StartSaga1 : ICommand + { + } + + [Serializable] + public class StartSaga2 : ICommand + { + } + + public class Saga1Timeout : IMessage + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_timeout_hit_not_found_saga.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_timeout_hit_not_found_saga.cs new file mode 100644 index 000000000..8d8240ddc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_timeout_hit_not_found_saga.cs @@ -0,0 +1,90 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + + public class When_timeout_hit_not_found_saga : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_fire_notfound_for_tm() + { + var context = Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new StartSaga()))) + .Done(c => c.NotFoundHandlerCalledForRegularMessage) + .Run(); + + + Assert.False(context.NotFoundHandlerCalledForTimeout); + + } + + public class Context : ScenarioContext + { + public bool NotFoundHandlerCalledForRegularMessage { get; set; } + public bool NotFoundHandlerCalledForTimeout { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup(); + } + + public class MySaga : Saga, + IAmStartedByMessages, IHandleSagaNotFound, + IHandleTimeouts, + IHandleMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga message) + { + //this will cause the message to be delivered right away + RequestTimeout(TimeSpan.Zero); + Bus.SendLocal(new SomeOtherMessage()); + + MarkAsComplete(); + } + + public class MySagaData : ContainSagaData + { + public virtual Guid DataId { get; set; } + } + + public class MyTimeout { } + + public void Handle(object message) + { + if (message is SomeOtherMessage) + { + Context.NotFoundHandlerCalledForRegularMessage = true; + } + + + if (message is MyTimeout) + { + Context.NotFoundHandlerCalledForTimeout = true; + } + + } + + public void Handle(SomeOtherMessage message) + { + + } + + public void Timeout(MyTimeout state) + { + + } + } + } + + public class StartSaga : IMessage { } + public class SomeOtherMessage : IMessage { } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_two_sagas_subscribe_to_the_same_event.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_two_sagas_subscribe_to_the_same_event.cs new file mode 100644 index 000000000..d869b2b17 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_two_sagas_subscribe_to_the_same_event.cs @@ -0,0 +1,161 @@ + +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using PubSub; + using Saga; + using ScenarioDescriptors; + + // Repro for issue https://github.com/NServiceBus/NServiceBus/issues/1277 + public class When_two_sagas_subscribe_to_the_same_event : NServiceBusAcceptanceTest + { + [Test] + public void Should_invoke_all_handlers_on_all_sagas() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("Saga1")) + { + context.Subscribed = true; + } + })) + .When(c => true, bus => bus.SendLocal(new StartSaga2 + { + DataId = Guid.NewGuid() + })) + ) + .WithEndpoint() + .Done(c => c.DidSaga1EventHandlerGetInvoked && c.DidSaga2EventHandlerGetInvoked) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidSaga1EventHandlerGetInvoked && c.DidSaga2EventHandlerGetInvoked)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool Subscribed { get; set; } + public bool DidSaga1EventHandlerGetInvoked { get; set; } + public bool DidSaga2EventHandlerGetInvoked { get; set; } + } + + public class EndpointThatHandlesAMessageAndPublishesEvent : EndpointConfigurationBuilder + { + public EndpointThatHandlesAMessageAndPublishesEvent() + { + EndpointSetup(); + } + + class OpenGroupCommandHandler : IHandleMessages + { + public IBus Bus { get; set; } + + public void Handle(OpenGroupCommand message) + { + Console.WriteLine("Received OpenGroupCommand for DataId:{0} ... and publishing GroupPendingEvent", message.DataId); + Bus.Publish(new GroupPendingEvent { DataId = message.DataId }); + } + } + } + + public class EndpointThatHostsTwoSagas : EndpointConfigurationBuilder + { + public EndpointThatHostsTwoSagas() + { + EndpointSetup() + .AddMapping(typeof(EndpointThatHandlesAMessageAndPublishesEvent)) + .AddMapping(typeof(EndpointThatHandlesAMessageAndPublishesEvent)); + } + + public class Saga1 : Saga, IAmStartedByMessages, IHandleMessages + { + public Context Context { get; set; } + + public void Handle(GroupPendingEvent message) + { + Data.DataId = message.DataId; + Console.Out.WriteLine("Saga1 received GroupPendingEvent for DataId: {0}", message.DataId); + Context.DidSaga1EventHandlerGetInvoked = true; + Bus.SendLocal(new CompleteSaga1Now { DataId = message.DataId }); + } + + public void Handle(CompleteSaga1Now message) + { + Console.Out.WriteLine("Saga1 received CompleteSaga1Now for DataId:{0} and MarkAsComplete", message.DataId); + + MarkAsComplete(); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + + public class MySaga1Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + } + + public class Saga2 : Saga, IAmStartedByMessages, IHandleMessages + { + public Context Context { get; set; } + + public void Handle(StartSaga2 message) + { + var dataId = Guid.NewGuid(); + Console.Out.WriteLine("Saga2 sending OpenGroupCommand for DataId: {0}", dataId); + Data.DataId = dataId; + Bus.Send(new OpenGroupCommand { DataId = dataId }); + } + + public void Handle(GroupPendingEvent message) + { + Context.DidSaga2EventHandlerGetInvoked = true; + Console.Out.WriteLine("Saga2 received GroupPendingEvent for DataId: {0} and MarkAsComplete", message.DataId); + MarkAsComplete(); + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + + public class MySaga2Data : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + } + } + + [Serializable] + public class GroupPendingEvent : IEvent + { + public Guid DataId { get; set; } + } + + public class OpenGroupCommand : ICommand + { + public Guid DataId { get; set; } + } + + [Serializable] + public class StartSaga2 : ICommand + { + public Guid DataId { get; set; } + } + + public class CompleteSaga1Now : ICommand + { + public Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_a_received_message_for_timeout.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_a_received_message_for_timeout.cs new file mode 100644 index 000000000..0e0f0c65d --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_a_received_message_for_timeout.cs @@ -0,0 +1,117 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using PubSub; + using Saga; + using ScenarioDescriptors; + + public class When_using_a_received_message_for_timeout : NServiceBusAcceptanceTest + { + [Test] + public void Timeout_should_be_received_after_expiration() + { + Scenario.Define(() => new Context {Id = Guid.NewGuid()}) + .WithEndpoint(b => + { + b.Given((bus, context) => + { + if (!Feature.IsEnabled()) + { + bus.SendLocal(new StartSagaMessage { SomeId = context.Id }); + } + else + { + Subscriptions.OnEndpointSubscribed(s => bus.SendLocal(new StartSagaMessage { SomeId = context.Id })); + } + + }); + + b.When(context => context.StartSagaMessageReceived, + (bus, context) => bus.Publish(new SomeEvent { SomeId = context.Id })); + + }) + .Done(c => c.TimeoutReceived) + .Repeat(r => r.For(Transports.Default)) + .Run(); + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + + public bool StartSagaMessageReceived { get; set; } + + public bool SomeEventReceived { get; set; } + + public bool TimeoutReceived { get; set; } + } + + public class SagaEndpoint : EndpointConfigurationBuilder + { + public SagaEndpoint() + { + EndpointSetup(c => c.RavenSagaPersister() + .UnicastBus()) + .AddMapping(typeof (SagaEndpoint)); + } + + public class TestSaga : Saga, IAmStartedByMessages, + IHandleMessages, IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(StartSagaMessage message) + { + Data.SomeId = message.SomeId; + Context.StartSagaMessageReceived = true; + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.SomeId) + .ToSaga(s => s.SomeId); + ConfigureMapping(m => m.SomeId) + .ToSaga(s => s.SomeId); + } + + public void Handle(SomeEvent message) + { + RequestTimeout(TimeSpan.FromMilliseconds(100), message); + Context.SomeEventReceived = true; + } + + public void Timeout(SomeEvent message) + { + Context.TimeoutReceived = true; + MarkAsComplete(); + } + } + + public class TestSagaData : IContainSagaData + { + public virtual Guid Id { get; set; } + public virtual string Originator { get; set; } + public virtual string OriginalMessageId { get; set; } + + [Unique] + public virtual Guid SomeId { get; set; } + } + } + + [Serializable] + public class StartSagaMessage : ICommand + { + public Guid SomeId { get; set; } + } + + [Serializable] + public class SomeEvent : IEvent + { + public Guid SomeId { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_contain_saga_data.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_contain_saga_data.cs new file mode 100644 index 000000000..398281a52 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Sagas/When_using_contain_saga_data.cs @@ -0,0 +1,80 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using Saga; + using ScenarioDescriptors; + + // Repro for #SB-191 + public class When_using_contain_saga_data : NServiceBusAcceptanceTest + { + [Test] + public void Should_handle_timeouts_properly() + { + Scenario.Define() + .WithEndpoint( + b => b.Given(bus => bus.SendLocal(new StartSaga {DataId = Guid.NewGuid()}))) + .Done(c => c.DidAllSagaInstancesReceiveTimeouts) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.DidAllSagaInstancesReceiveTimeouts)) + .Run(); + } + + public class Context : ScenarioContext + { + public bool DidAllSagaInstancesReceiveTimeouts { get; set; } + } + + public class EndpointThatHostsASaga : EndpointConfigurationBuilder + { + public EndpointThatHostsASaga() + { + EndpointSetup(); + } + + public class MySaga : Saga, + IAmStartedByMessages, + IHandleTimeouts + { + public Context Context { get; set; } + + public void Handle(StartSaga message) + { + Data.DataId = message.DataId; + + RequestTimeout(TimeSpan.FromSeconds(5), new TimeHasPassed()); + } + + public void Timeout(TimeHasPassed state) + { + MarkAsComplete(); + + Context.DidAllSagaInstancesReceiveTimeouts = true; + } + + public override void ConfigureHowToFindSaga() + { + ConfigureMapping(m => m.DataId).ToSaga(s => s.DataId); + } + + public class MySagaData : ContainSagaData + { + [Unique] + public virtual Guid DataId { get; set; } + } + + public class TimeHasPassed + { + } + } + } + + [Serializable] + public class StartSaga : ICommand + { + public Guid DataId { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllBuilders.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllBuilders.cs new file mode 100644 index 000000000..81b96aa3f --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllBuilders.cs @@ -0,0 +1,17 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using AcceptanceTesting.Support; + + public class AllBuilders : ScenarioDescriptor + { + public AllBuilders() + { + Add(Builders.Autofac); + Add(Builders.Windsor); + Add(Builders.Ninject); + Add(Builders.Spring); + Add(Builders.StructureMap); + Add(Builders.Unity); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSerializers.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSerializers.cs new file mode 100644 index 000000000..f786b878e --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSerializers.cs @@ -0,0 +1,15 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using AcceptanceTesting.Support; + + public class AllSerializers : ScenarioDescriptor + { + public AllSerializers() + { + Add(Serializers.Bson); + Add(Serializers.Json); + Add(Serializers.Xml); + Add(Serializers.Binary); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSubscriptionStorages.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSubscriptionStorages.cs new file mode 100644 index 000000000..c78dde444 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllSubscriptionStorages.cs @@ -0,0 +1,14 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using AcceptanceTesting.Support; + + public class AllSubscriptionStorages : ScenarioDescriptor + { + public AllSubscriptionStorages() + { + Add(SubscriptionStorages.InMemory); + Add(SubscriptionStorages.Raven); + Add(SubscriptionStorages.Msmq); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllTransports.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllTransports.cs new file mode 100644 index 000000000..400816d64 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/AllTransports.cs @@ -0,0 +1,121 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using AcceptanceTesting.Support; + using Hosting.Helpers; + using NServiceBus.Transports; + + public class AllTransports : ScenarioDescriptor + { + public AllTransports() + { + AddRange(ActiveTransports); + } + + static IEnumerable ActiveTransports + { + get + { + if (activeTransports == null) + { + //temporary fix until we can get rid of the "AllTransports" all together + activeTransports = new List + { + Transports.Default + }; + } + + return activeTransports; + } + } + + static ICollection activeTransports; + } + + public class AllDtcTransports : AllTransports + { + public AllDtcTransports() + { + AllTransportsFilter.Run(t => t.HasSupportForDistributedTransactions.HasValue + && !t.HasSupportForDistributedTransactions.Value, Remove); + + // todo set HasSupportForDistributedTransactions to false on Rabbit MQ + AllTransportsFilter.Run(t => t.GetType().Name.Contains("RabbitMQ"), Remove); + } + } + + public class AllBrokerTransports : AllTransports + { + public AllBrokerTransports() + { + AllTransportsFilter.Run(t => !t.HasNativePubSubSupport, Remove); + } + } + + public class AllTransportsWithCentralizedPubSubSupport : AllTransports + { + public AllTransportsWithCentralizedPubSubSupport() + { + AllTransportsFilter.Run(t => !t.HasSupportForCentralizedPubSub, Remove); + } + } + + public class AllTransportsWithMessageDrivenPubSub : AllTransports + { + public AllTransportsWithMessageDrivenPubSub() + { + AllTransportsFilter.Run(t => t.HasNativePubSubSupport, Remove); + } + } + + public class TypeScanner + { + + public static IEnumerable GetAllTypesAssignableTo() + { + return AvailableAssemblies.SelectMany(a => a.GetTypes()) + .Where(t => typeof (T).IsAssignableFrom(t) && t != typeof(T)) + .ToList(); + } + + static IEnumerable AvailableAssemblies + { + get + { + if (assemblies == null) + { + var result = new AssemblyScanner().GetScannableAssemblies(); + + assemblies = result.Assemblies; + } + + return assemblies; + } + } + + static List assemblies; + } + + public static class AllTransportsFilter + { + public static void Run(Func condition, Func remove) + { + foreach (var rundescriptor in Transports.AllAvailable) + { + var transportAssemblyQualifiedName = rundescriptor.Settings["Transport"]; + var type = Type.GetType(transportAssemblyQualifiedName); + if (type != null) + { + var transport = Activator.CreateInstance(type) as TransportDefinition; + if (condition(transport)) + { + remove(rundescriptor); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Builders.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Builders.cs new file mode 100644 index 000000000..6f97c97d5 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Builders.cs @@ -0,0 +1,61 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System.Collections.Generic; + using System.Linq; + using AcceptanceTesting.Support; + using ObjectBuilder.Common; + + public static class Builders + { + static IList availableTransports; + + public static IEnumerable AllAvailable + { + get { return availableTransports ?? (availableTransports = GetAllAvailable().ToList()); } + } + + static IEnumerable GetAllAvailable() + { + var builders = TypeScanner.GetAllTypesAssignableTo() + .Where(t => !t.Assembly.FullName.StartsWith("NServiceBus.Core") && //exclude the default builder + t.Name.EndsWith("ObjectBuilder") ) //exclude the ninject child container + .ToList(); + + return from builder in builders let name = builder.Name.Replace("ObjectBuilder", "") select (new RunDescriptor + { + Key = name, + Settings = new Dictionary { { "Builder", builder.AssemblyQualifiedName } } + }); + } + + public static RunDescriptor Autofac + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Autofac"); } + } + + public static RunDescriptor Ninject + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Ninject"); } + } + + public static RunDescriptor StructureMap + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "StructureMap"); } + } + + public static RunDescriptor Windsor + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Windsor"); } + } + + public static RunDescriptor Spring + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Spring"); } + } + + public static RunDescriptor Unity + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Unity"); } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SagaPersisters.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SagaPersisters.cs new file mode 100644 index 000000000..6face597d --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SagaPersisters.cs @@ -0,0 +1,61 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System; + using System.Collections.Generic; + using System.Linq; + using AcceptanceTesting.Support; + using Persistence.InMemory.SagaPersister; + using Persistence.Raven.SagaPersister; + using Saga; + + public class SagaPersisters : ScenarioDescriptor + { + public SagaPersisters() + { + var persisters = AvailablePersisters; + + foreach (var persister in persisters) + { + Add(new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary { { "SagaPersister", persister.AssemblyQualifiedName } } + }); + } + } + + static List AvailablePersisters + { + get + { + var persisters = TypeScanner.GetAllTypesAssignableTo() + .Where(t => !t.IsInterface) + .ToList(); + return persisters; + } + } + + public static RunDescriptor Default + { + get + { + var persisters = AvailablePersisters; + var persister = persisters.FirstOrDefault(p => p != typeof(InMemorySagaPersister) && p != typeof(RavenSagaPersister)); + + if (persister == null) + { + persister = typeof(InMemorySagaPersister); + } + + return new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary + { + {"SagaPersister", persister.AssemblyQualifiedName} + } + }; + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Serializers.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Serializers.cs new file mode 100644 index 000000000..c15b82e11 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Serializers.cs @@ -0,0 +1,59 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System.Collections.Generic; + using AcceptanceTesting.Support; + using NServiceBus.Serializers.Binary; + using NServiceBus.Serializers.Json; + using NServiceBus.Serializers.XML; + + public static class Serializers + { + public static readonly RunDescriptor Binary = new RunDescriptor + { + Key = "Binary", + Settings = + new Dictionary + { + { + "Serializer", typeof (BinaryMessageSerializer).AssemblyQualifiedName + } + } + }; + + public static readonly RunDescriptor Bson = new RunDescriptor + { + Key = "Bson", + Settings = + new Dictionary + { + { + "Serializer", typeof (BsonMessageSerializer).AssemblyQualifiedName + } + } + }; + + public static readonly RunDescriptor Xml = new RunDescriptor + { + Key = "Xml", + Settings = + new Dictionary + { + { + "Serializer", typeof (XmlMessageSerializer).AssemblyQualifiedName + } + } + }; + + public static readonly RunDescriptor Json = new RunDescriptor + { + Key = "Json", + Settings = + new Dictionary + { + { + "Serializer", typeof (JsonMessageSerializer).AssemblyQualifiedName + } + } + }; + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionPersisters.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionPersisters.cs new file mode 100644 index 000000000..3cc40078d --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionPersisters.cs @@ -0,0 +1,62 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System; + using System.Collections.Generic; + using System.Linq; + using AcceptanceTesting.Support; + using Persistence.InMemory.SubscriptionStorage; + using Persistence.Msmq.SubscriptionStorage; + using Persistence.Raven.SubscriptionStorage; + using Unicast.Subscriptions.MessageDrivenSubscriptions; + + public class SubscriptionPersisters : ScenarioDescriptor + { + public SubscriptionPersisters() + { + var persisters = AvailablePersisters; + + foreach (var persister in persisters) + { + Add(new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary { { "SubscriptionStorage", persister.AssemblyQualifiedName } } + }); + } + } + + static List AvailablePersisters + { + get + { + var persisters = TypeScanner.GetAllTypesAssignableTo() + .Where(t => !t.IsInterface) + .ToList(); + return persisters; + } + } + + public static RunDescriptor Default + { + get + { + var persisters = AvailablePersisters; + var persister = persisters.FirstOrDefault(p => p != typeof(InMemorySubscriptionStorage) && p != typeof(RavenSubscriptionStorage) && p != typeof(MsmqSubscriptionStorage)); + + if (persister == null) + { + persister = typeof(InMemorySubscriptionStorage); + } + + return new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary + { + {"SubscriptionStorage", persister.AssemblyQualifiedName} + } + }; + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionStorages.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionStorages.cs new file mode 100644 index 000000000..8ee371729 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/SubscriptionStorages.cs @@ -0,0 +1,52 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System.Collections.Generic; + using AcceptanceTesting.Support; + using Persistence.InMemory.SubscriptionStorage; + using Persistence.Msmq.SubscriptionStorage; + using Persistence.Raven.SubscriptionStorage; + + public static class SubscriptionStorages + { + public static readonly RunDescriptor InMemory = new RunDescriptor + { + Key = "InMemorySubscriptionStorage", + Settings = + new Dictionary + { + { + "SubscriptionStorage", + typeof (InMemorySubscriptionStorage).AssemblyQualifiedName + } + } + }; + + + public static readonly RunDescriptor Raven = new RunDescriptor + { + Key = "RavenSubscriptionStorage", + Settings = + new Dictionary + { + { + "SubscriptionStorage", + typeof (RavenSubscriptionStorage).AssemblyQualifiedName + } + } + }; + + + public static readonly RunDescriptor Msmq = new RunDescriptor + { + Key = "MsmqSubscriptionStorage", + Settings = + new Dictionary + { + { + "SubscriptionStorage", + typeof (MsmqSubscriptionStorage).AssemblyQualifiedName + } + } + }; + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/TimeoutPersisters.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/TimeoutPersisters.cs new file mode 100644 index 000000000..025e25f88 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/TimeoutPersisters.cs @@ -0,0 +1,62 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System; + using System.Collections.Generic; + using System.Linq; + using AcceptanceTesting.Support; + using Persistence.InMemory.TimeoutPersister; + using Persistence.Raven.TimeoutPersister; + using Timeout.Core; + + public class TimeoutPersisters : ScenarioDescriptor + { + public TimeoutPersisters() + { + var persisters = AvailablePersisters; + + foreach (var persister in persisters) + { + Add(new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary { { "TimeoutPersister", persister.AssemblyQualifiedName } } + }); + } + } + + static List AvailablePersisters + { + get + { + var persisters = TypeScanner.GetAllTypesAssignableTo() + .Where(t => !t.IsInterface) + .ToList(); + return persisters; + } + } + + public static RunDescriptor Default + { + get + { + var persisters = AvailablePersisters; + var persister = persisters.FirstOrDefault(p => p != typeof(InMemoryTimeoutPersistence) && p != typeof(RavenTimeoutPersistence)); + + if (persister == null) + { + persister = typeof(InMemoryTimeoutPersistence); + } + + return new RunDescriptor + { + Key = persister.Name, + Settings = new Dictionary + { + {"TimeoutPersister", persister.AssemblyQualifiedName} + } + }; + } + } + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Transports.cs b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Transports.cs new file mode 100644 index 000000000..ac18147df --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/ScenarioDescriptors/Transports.cs @@ -0,0 +1,114 @@ +namespace NServiceBus.AcceptanceTests.ScenarioDescriptors +{ + using System; + using System.Collections.Generic; + using System.Linq; + using AcceptanceTesting.Support; + using NServiceBus.Transports; + + public static class Transports + { + public static IEnumerable AllAvailable + { + get + { + if (availableTransports == null) + { + availableTransports = GetAllAvailable().ToList(); + } + + return availableTransports; + } + } + + + public static RunDescriptor Default + { + get + { + var specificTransport = Environment.GetEnvironmentVariable("Transport.UseSpecific"); + + if (!string.IsNullOrEmpty(specificTransport)) + return AllAvailable.Single(r => r.Key == specificTransport); + + var transportsOtherThanMsmq = AllAvailable.Where(t => t != Msmq); + + if (transportsOtherThanMsmq.Count() == 1) + return transportsOtherThanMsmq.First(); + + return Msmq; + } + } + + public static RunDescriptor ActiveMQ + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "ActiveMQ"); } + } + + public static RunDescriptor Msmq + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "Msmq"); } + } + + public static RunDescriptor RabbitMQ + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "RabbitMQ"); } + } + + + + public static RunDescriptor SqlServer + { + get { return AllAvailable.SingleOrDefault(r => r.Key == "SqlServer"); } + } + + static IEnumerable GetAllAvailable() + { + var foundTransportDefinitions = TypeScanner.GetAllTypesAssignableTo(); + + + foreach (var transportDefinitionType in foundTransportDefinitions) + { + var key = transportDefinitionType.Name; + + var runDescriptor = new RunDescriptor + { + Key = key, + Settings = + new Dictionary + { + {"Transport", transportDefinitionType.AssemblyQualifiedName} + } + }; + + var connectionString = Environment.GetEnvironmentVariable(key + ".ConnectionString"); + + if (string.IsNullOrEmpty(connectionString) && DefaultConnectionStrings.ContainsKey(key)) + connectionString = DefaultConnectionStrings[key]; + + + if (!string.IsNullOrEmpty(connectionString)) + { + runDescriptor.Settings.Add("Transport.ConnectionString", connectionString); + yield return runDescriptor; + } + else + { + Console.Out.WriteLine("No connection string found for transport: {0}, test will not be executed for this transport", key); + } + } + } + + static IList availableTransports; + + static readonly Dictionary DefaultConnectionStrings = new Dictionary + { + {"RabbitMQ", "host=localhost"}, + {"SqlServer", @"Server=localhost\sqlexpress;Database=nservicebus;Trusted_Connection=True;"}, + {"ActiveMQ", @"ServerUrl=activemq:tcp://localhost:61616"}, + {"Msmq", @"cacheSendConnection=false;journal=false;"} + }; + + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Scheduling/When_scheduling_a_recurring_task.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Scheduling/When_scheduling_a_recurring_task.cs new file mode 100644 index 000000000..ab24ef9dd --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Scheduling/When_scheduling_a_recurring_task.cs @@ -0,0 +1,55 @@ +namespace NServiceBus.AcceptanceTests.Sagas +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_scheduling_a_recurring_task : NServiceBusAcceptanceTest + { + [Test] + public void Should_execute_the_task() + { + Scenario.Define() + .WithEndpoint() + .Done(c => c.ScheduleActionInvoked) + .Repeat(r => r.For(Transports.Default)) + .Run(TimeSpan.FromSeconds(60)); + } + + public class Context : ScenarioContext + { + public bool ScheduleActionInvoked { get; set; } + } + + public class SchedulingEndpoint : EndpointConfigurationBuilder + { + public SchedulingEndpoint() + { + EndpointSetup(); + } + + class SetupScheduledAction : IWantToRunWhenBusStartsAndStops + { + public void Start() + { + Schedule.Every(TimeSpan.FromSeconds(5)) + .Action("MyTask", () => + { + Console.Out.WriteLine("Task invoked"); + Configure.Instance.Builder.Build() + .ScheduleActionInvoked = true; + }); + } + + public void Stop() + { + + } + } + } + } + + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_Deferring_a_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_Deferring_a_message.cs new file mode 100644 index 000000000..0fd674136 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_Deferring_a_message.cs @@ -0,0 +1,52 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_Deferring_a_message : NServiceBusAcceptanceTest + { + [Test] + public void Message_should_be_received() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Defer(TimeSpan.FromSeconds(3), new MyMessage()))) + .Done(c => c.WasCalled) + .Run(); + + Assert.IsTrue(context.WasCalled); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + Context.WasCalled = true; + } + } + } + + [Serializable] + public class MyMessage : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_aborting_the_behavior_chain.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_aborting_the_behavior_chain.cs new file mode 100644 index 000000000..e7651a055 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_aborting_the_behavior_chain.cs @@ -0,0 +1,73 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + + public class When_aborting_the_behavior_chain : NServiceBusAcceptanceTest + { + [Test] + public void Subsequent_handlers_will_not_be_invoked() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.FirstHandlerInvoked) + .Run(); + + Assert.That(context.FirstHandlerInvoked, Is.True); + Assert.That(context.SecondHandlerInvoked, Is.False); + } + + public class Context : ScenarioContext + { + public bool FirstHandlerInvoked { get; set; } + public bool SecondHandlerInvoked { get; set; } + } + + [Serializable] + public class SomeMessage : IMessage { } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + class EnsureOrdering : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.Specify(First.Then()); + } + } + + class FirstHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(SomeMessage message) + { + Context.FirstHandlerInvoked = true; + + Bus.DoNotContinueDispatchingCurrentMessageToHandlers(); + } + } + + class SecondHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(SomeMessage message) + { + Context.SecondHandlerInvoked = true; + } + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_handling_current_message_later.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_handling_current_message_later.cs new file mode 100644 index 000000000..b8db860af --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_handling_current_message_later.cs @@ -0,0 +1,157 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using System.Linq; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + + public class When_handling_current_message_later : NServiceBusAcceptanceTest + { + [Test] + public void Should_commit_unit_of_work() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + Assert.That(context.AnotherMessageReceivedCount, Is.EqualTo(2), + "First handler sends a message to self, which should result in AnotherMessage being dispatched twice"); + } + + [Test, Description("NOTE the double negation - we should probably modify this behavior somehow")] + public void Should_not_not_execute_subsequent_handlers() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + Assert.That(context.ThirdHandlerInvocationCount, Is.EqualTo(2), + "Since calling HandleCurrentMessageLater does not discontinue message dispatch, the third handler should be called twice as well"); + } + + [Test] + public void Handlers_are_executed_in_the_right_order() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new SomeMessage()))) + .Done(c => c.Done) + .Run(); + + var events = context.Events; + CollectionAssert + .AreEquivalent(new[] + { + "FirstHandler:Executed", + "SecondHandler:Sending message to the back of the queue", + "ThirdHandler:Executed", + "FirstHandler:Executed", + "SecondHandler:Handling the message this time", + "ThirdHandler:Executed" + }, + events); + } + + public class Context : ScenarioContext + { + public string[] Events { get; set; } + + public bool SomeMessageHasBeenRequeued { get; set; } + + public bool Done + { + get { return ThirdHandlerInvocationCount >= 2 && AnotherMessageReceivedCount == 2; } + } + + public int AnotherMessageReceivedCount { get; set; } + + public int ThirdHandlerInvocationCount { get; set; } + } + + [Serializable] + public class SomeMessage : IMessage { } + + [Serializable] + public class AnotherMessage : IMessage { } + + public class MyEndpoint : EndpointConfigurationBuilder + { + public MyEndpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + class EnsureOrdering : ISpecifyMessageHandlerOrdering + { + public void SpecifyOrder(Order order) + { + order.Specify(First.Then().AndThen()); + } + } + + class FirstHandler : IHandleMessages, IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + + public void Handle(SomeMessage message) + { + Context.RegisterEvent("FirstHandler:Executed"); + Bus.SendLocal(new AnotherMessage()); + } + + public void Handle(AnotherMessage message) + { + Context.AnotherMessageReceivedCount++; + } + } + + class SecondHandler : IHandleMessages + { + public Context Context { get; set; } + public IBus Bus { get; set; } + public void Handle(SomeMessage message) + { + if (!Context.SomeMessageHasBeenRequeued) + { + Context.RegisterEvent("SecondHandler:Sending message to the back of the queue"); + Bus.HandleCurrentMessageLater(); + Context.SomeMessageHasBeenRequeued = true; + } + else + { + Context.RegisterEvent("SecondHandler:Handling the message this time"); + } + } + } + + class ThirdHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(SomeMessage message) + { + Context.RegisterEvent("ThirdHandler:Executed"); + Context.ThirdHandlerInvocationCount++; + } + } + } + } + + static class ContextEx + { + public static void RegisterEvent(this When_handling_current_message_later.Context context, string description) + { + context.Events = context.Events + .Concat(new[] {description}) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_registering_a_callback_for_a_local_message.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_registering_a_callback_for_a_local_message.cs new file mode 100644 index 000000000..976a560cc --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_registering_a_callback_for_a_local_message.cs @@ -0,0 +1,64 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_registering_a_callback_for_a_local_message : NServiceBusAcceptanceTest + { + [Test] + public void Should_trigger_the_callback_when_the_response_comes_back() + { + Scenario.Define() + .WithEndpoint(b=> b.Given( + (bus, context) => bus.SendLocal(new MyRequest()).Register(r => + { + Assert.True(context.HandlerGotTheRequest); + context.CallbackFired = true; + }))) + .Done(c => c.CallbackFired) + .Repeat(r =>r.For(Transports.Default)) + .Should(c => + { + Assert.True(c.CallbackFired); + Assert.True(c.HandlerGotTheRequest); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool HandlerGotTheRequest { get; set; } + + public bool CallbackFired { get; set; } + } + + public class EndpointWithLocalCallback : EndpointConfigurationBuilder + { + public EndpointWithLocalCallback() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + public class MyRequestHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest request) + { + Assert.False(Context.CallbackFired); + Context.HandlerGotTheRequest = true; + + Bus.Return(1); + } + } + } + + [Serializable] + public class MyRequest : IMessage{} + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs new file mode 100644 index 000000000..751ae3d7f --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint.cs @@ -0,0 +1,100 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using System.Threading; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_sending_a_message_that_is_registered_multiple_times_to_another_endpoint : NServiceBusAcceptanceTest + { + [Test] + public void First_registration_should_be_the_final_destination() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.Send(new MyCommand1()))) + .WithEndpoint() + .WithEndpoint() + .Done(c => c.WasCalled1 || c.WasCalled2) + .Run(); + + Assert.IsTrue(context.WasCalled1); + Assert.IsFalse(context.WasCalled2); + } + + public class Context : ScenarioContext + { + public bool WasCalled1 { get; set; } + public bool WasCalled2 { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ) + .AddMapping(typeof(Receiver1)) + .AddMapping(typeof(Receiver2)); + } + } + + public class Receiver1 : EndpointConfigurationBuilder + { + public Receiver1() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyBaseCommand message) + { + Context.WasCalled1 = true; + Thread.Sleep(2000); // Just to be sure the other receiver is finished + } + } + } + + public class Receiver2 : EndpointConfigurationBuilder + { + public Receiver2() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyBaseCommand message) + { + Context.WasCalled2 = true; + Thread.Sleep(2000); // Just to be sure the other receiver is finished + } + } + } + + [Serializable] + public abstract class MyBaseCommand : ICommand + { + } + + [Serializable] + public class MyCommand1 : MyBaseCommand + { + } + + [Serializable] + public class MyCommand2 : MyBaseCommand + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_to_another_endpoint.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_to_another_endpoint.cs new file mode 100644 index 000000000..68cc70309 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_to_another_endpoint.cs @@ -0,0 +1,99 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using System.Collections.Generic; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_a_message_to_another_endpoint : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_the_message() + { + Scenario.Define(() => new Context { Id = Guid.NewGuid() }) + .WithEndpoint(b => b.Given((bus, context) => + { + bus.OutgoingHeaders["MyStaticHeader"] = "StaticHeaderValue"; + bus.Send(m => + { + m.Id = context.Id; + m.SetHeader("MyHeader", "MyHeaderValue"); + }); + })) + .WithEndpoint() + .Done(c => c.WasCalled) + .Repeat(r =>r.For(Serializers.Binary) + .For() + ) + .Should(c => + { + Assert.True(c.WasCalled, "The message handler should be called"); + Assert.AreEqual(1, c.TimesCalled, "The message handler should only be invoked once"); + Assert.True(c.ReceivedHeaders[Headers.OriginatingEndpoint].Contains("Sender"), "The sender should attach its endpoint name as a header"); + Assert.True(c.ReceivedHeaders[Headers.ProcessingEndpoint].Contains("Receiver"), "The receiver should attach its endpoint name as a header"); + Assert.AreEqual("StaticHeaderValue",c.ReceivedHeaders["MyStaticHeader"], "Static headers should be attached to outgoing messages"); + Assert.AreEqual("MyHeaderValue", c.MyHeader, "Static headers should be attached to outgoing messages"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + + public int TimesCalled { get; set; } + + public IDictionary ReceivedHeaders { get; set; } + + public Guid Id { get; set; } + + public string MyHeader { get; set; } + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ) + .AddMapping(typeof(Receiver)); + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + } + + [Serializable] + public class MyMessage : ICommand + { + public Guid Id { get; set; } + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + if (Context.Id != message.Id) + return; + + Context.TimesCalled++; + + Context.MyHeader = message.GetHeader("MyHeader"); + + Context.ReceivedHeaders = Bus.CurrentMessageContext.Headers; + + Context.WasCalled = true; + } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_with_no_correlation_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_with_no_correlation_id.cs new file mode 100644 index 000000000..d90819bef --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_sending_a_message_with_no_correlation_id.cs @@ -0,0 +1,73 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class When_sending_a_message_with_no_correlation_id : NServiceBusAcceptanceTest + { + [Test] + public void Should_use_the_message_id_as_the_correlation_id() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, new MyRequest()))) + .Done(c => c.GotRequest) + .Run(); + + Assert.AreEqual(context.MessageIdReceived, context.CorrelationIdReceived, "Correlation id should match MessageId"); + } + + public class Context : ScenarioContext + { + public string MessageIdReceived { get; set; } + public bool GotRequest { get; set; } + public string CorrelationIdReceived { get; set; } + } + + public class CorrelationEndpoint : EndpointConfigurationBuilder + { + public CorrelationEndpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + class GetValueOfIncomingCorrelationId:IMutateIncomingTransportMessages,INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.CorrelationIdReceived = transportMessage.CorrelationId; + Context.MessageIdReceived = transportMessage.Id; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest response) + { + Context.GotRequest = true; + } + } + } + + + [Serializable] + public class MyRequest : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_custom_correlation_id.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_custom_correlation_id.cs new file mode 100644 index 000000000..b23881af6 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_custom_correlation_id.cs @@ -0,0 +1,74 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using MessageMutator; + using NUnit.Framework; + + public class When_using_a_custom_correlation_id : NServiceBusAcceptanceTest + { + static string CorrelationId = "my_custom_correlation_id"; + + [Test] + public void Should_use_the_given_id_as_the_transport_level_correlation_id() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given(bus => bus.Send(Address.Local, CorrelationId, new MyRequest()))) + .Done(c => c.GotRequest) + .Run(); + + Assert.AreEqual(CorrelationId, context.CorrelationIdReceived, "Correlation ids should match"); + } + + public class Context : ScenarioContext + { + public bool GotRequest { get; set; } + + public string CorrelationIdReceived { get; set; } + } + + public class CorrelationEndpoint : EndpointConfigurationBuilder + { + public CorrelationEndpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + class GetValueOfIncomingCorrelationId:IMutateIncomingTransportMessages,INeedInitialization + { + public Context Context { get; set; } + + public void MutateIncoming(TransportMessage transportMessage) + { + Context.CorrelationIdReceived = transportMessage.CorrelationId; + } + + public void Init() + { + Configure.Component(DependencyLifecycle.InstancePerCall); + } + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest response) + { + Context.GotRequest = true; + } + } + } + + + [Serializable] + public class MyRequest : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_greedy_convention.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_greedy_convention.cs new file mode 100644 index 000000000..7f08d4c3b --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_greedy_convention.cs @@ -0,0 +1,71 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_using_a_greedy_convention : NServiceBusAcceptanceTest + { + [Test] + public void Should_receive_the_message() + { + var scenarioContext = new Context + { + Id = Guid.NewGuid() + }; + Scenario.Define( scenarioContext ) + .WithEndpoint(b => b.Given((bus, context) => bus.SendLocal(new MyMessage + { + Id = context.Id + }))) + .Done(c => c.WasCalled) + .Run(); + + Assert.True( scenarioContext.WasCalled, "The message handler should be called" ); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + + public Guid Id { get; set; } + } + + public class EndPoint : EndpointConfigurationBuilder + { + public EndPoint() + { + EndpointSetup(c => + { + c.UseTransport(ConnectionStringWith_test_schema); + c.DefiningMessagesAs(MessageConvention); + }); + } + + static bool MessageConvention(Type t) + { + return t.Namespace != null && + (t.Namespace.EndsWith(".Messages") || (t == typeof(MyMessage))); + } + } + + [Serializable] + public class MyMessage + { + public Guid Id { get; set; } + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + public void Handle(MyMessage message) + { + if (Context.Id != message.Id) + return; + + Context.WasCalled = true; + } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs new file mode 100644 index 000000000..a05945604 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_a_message_with_TimeToBeReceived_has_not_expired.cs @@ -0,0 +1,53 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + + public class When_using_a_message_with_TimeToBeReceived_has_not_expired : NServiceBusAcceptanceTest + { + [Test] + public void Message_should_be_received() + { + var context = new Context(); + + Scenario.Define(context) + .WithEndpoint(b => b.Given((bus, c) => bus.SendLocal(new MyMessage()))) + .Done(c => c.WasCalled) + .Run(); + + Assert.IsTrue(context.WasCalled); + } + + public class Context : ScenarioContext + { + public bool WasCalled { get; set; } + } + + public class Endpoint : EndpointConfigurationBuilder + { + public Endpoint() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyMessage message) + { + Context.WasCalled = true; + } + } + } + + [Serializable] + [TimeToBeReceived("00:00:10")] + public class MyMessage : IMessage + { + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_callbacks_in_a_scaleout_scenario.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_callbacks_in_a_scaleout_scenario.cs new file mode 100644 index 000000000..16a3c8beb --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_callbacks_in_a_scaleout_scenario.cs @@ -0,0 +1,143 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + using Support; + + public class When_using_callbacks_in_a_scaleout_scenario : NServiceBusAcceptanceTest + { + [Test] + public void Each_client_should_have_a_unique_input_queue_to_avoid_processing_each_others_callbacks() + { + Scenario.Define(() => new Context{Id = Guid.NewGuid()}) + .WithEndpoint(b => + { + b.CustomConfig(c => RuntimeEnvironment.MachineNameAction = () => "ClientA"); + b.Given((bus, context) => bus.Send(new MyRequest + { + Id = context.Id, + Client = RuntimeEnvironment.MachineName + }) + .Register(r => context.CallbackAFired = true)); + }) + .WithEndpoint(b => + { + b.CustomConfig(c => RuntimeEnvironment.MachineNameAction = () => "ClientB"); + b.Given((bus, context) => bus.Send(new MyRequest + { + Id = context.Id, + Client = RuntimeEnvironment.MachineName + }) + .Register(r => context.CallbackBFired = true)); + }) + .WithEndpoint() + .Done(c => c.ClientAGotResponse && c.ClientBGotResponse) + .Repeat(r =>r.For() + ) + .Should(c => + { + Assert.True(c.CallbackAFired, "Callback on ClientA should fire"); + Assert.True(c.CallbackBFired, "Callback on ClientB should fire"); + Assert.False(c.ResponseEndedUpAtTheWrongClient, "One of the responses ended up at the wrong client"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public Guid Id { get; set; } + + public bool ClientAGotResponse { get; set; } + + public bool ClientBGotResponse { get; set; } + + public bool ResponseEndedUpAtTheWrongClient { get; set; } + + public bool CallbackAFired { get; set; } + + public bool CallbackBFired { get; set; } + } + + public class Client : EndpointConfigurationBuilder + { + public Client() + { + EndpointSetup(c => + { + c.UseTransport(ConnectionStringWith_test_schema); + Configure.ScaleOut(s => s.UseUniqueBrokerQueuePerMachine()); + }) + .AddMapping(typeof(Server)); + } + + public class MyResponseHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyResponse response) + { + if (Context.Id != response.Id) + return; + + if (RuntimeEnvironment.MachineName == "ClientA") + Context.ClientAGotResponse = true; + else + { + Context.ClientBGotResponse = true; + } + + if (RuntimeEnvironment.MachineName != response.Client) + Context.ResponseEndedUpAtTheWrongClient = true; + } + } + } + + public class Server : EndpointConfigurationBuilder + { + public Server() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_test_schema ) ); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + + public void Handle(MyRequest request) + { + if (Context.Id != request.Id) + return; + + + Bus.Reply(new MyResponse { Id = request.Id,Client = request.Client }); + } + } + } + + [Serializable] + public class MyRequest : IMessage + { + public Guid Id { get; set; } + + public string Client { get; set; } + } + + [Serializable] + public class MyResponse : IMessage + { + public Guid Id { get; set; } + + public string Client { get; set; } + } + + + + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_different_connection_strings_for_each_endpoint.cs b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_different_connection_strings_for_each_endpoint.cs new file mode 100644 index 000000000..d8edc0a7a --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/SqlCustomSchema/When_using_different_connection_strings_for_each_endpoint.cs @@ -0,0 +1,94 @@ +namespace NServiceBus.AcceptanceTests.SqlCustomSchema +{ + using System; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + + public class When_using_different_connection_strings_for_each_endpoint : NServiceBusAcceptanceTest + { + [Test] + public void Should_use_configured_connection_string_when_replying() + { + var context = new Context() + { + Id = Guid.NewGuid() + }; + + Scenario.Define(context) + .WithEndpoint() + .WithEndpoint(b => b.Given((bus, c) => bus.Send(new MyRequest + { + ContextId = c.Id + }))) + .Done(c => context.GotResponse) + .Run(); + } + + public class Sender : EndpointConfigurationBuilder + { + public Sender() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_sender_schema ) ) + .AddMapping(typeof(Receiver)); + } + + class MyReplyHandler : IHandleMessages + { + public IBus Bus { get; set; } + public Context Context { get; set; } + + public void Handle(MyReply message) + { + if (Context.Id != message.ContextId) + { + return; + } + Context.GotResponse = true; + } + } + } + + public class Receiver : EndpointConfigurationBuilder + { + public Receiver() + { + EndpointSetup( c => c.UseTransport( ConnectionStringWith_receiver_schema ) ); + } + + class MyEventHandler : IHandleMessages + { + public IBus Bus { get; set; } + public Context Context { get; set; } + + public void Handle(MyRequest message) + { + if (Context.Id != message.ContextId) + { + return; + } + Bus.Reply(new MyReply + { + ContextId = message.ContextId + }); + } + } + } + + class MyRequest : IMessage + { + public Guid ContextId { get; set; } + } + + class MyReply : IMessage + { + public Guid ContextId { get; set; } + } + + class Context : ScenarioContext + { + public bool GotResponse { get; set; } + public Guid Id { get; set; } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/FakePromotableResourceManager.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/FakePromotableResourceManager.cs new file mode 100644 index 000000000..8e3adc743 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/FakePromotableResourceManager.cs @@ -0,0 +1,54 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + + public class FakePromotableResourceManager : IPromotableSinglePhaseNotification, IEnlistmentNotification + { + public static Guid ResourceManagerId = Guid.Parse("6f057e24-a0d8-4c95-b091-b8dc9a916fa4"); + + public void Prepare(PreparingEnlistment preparingEnlistment) + { + preparingEnlistment.Prepared(); + } + + public void Commit(Enlistment enlistment) + { + enlistment.Done(); + } + + public void Rollback(Enlistment enlistment) + { + enlistment.Done(); + } + + public void InDoubt(Enlistment enlistment) + { + enlistment.Done(); + } + + + public void Initialize() + { + } + + public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment) + { + singlePhaseEnlistment.Committed(); + } + + public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment) + { + singlePhaseEnlistment.Done(); + } + + public byte[] Promote() + { + return TransactionInterop.GetTransmitterPropagationToken(new CommittableTransaction()); + + } + + + } + +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_disabled.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_disabled.cs new file mode 100644 index 000000000..095237133 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_disabled.cs @@ -0,0 +1,66 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_receiving_a_message_with_dtc_disabled : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_escalate_a_single_durable_rm_to_dtc_tx() + { + + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MyMessage()))) + .Done(c => c.HandlerInvoked) + .Repeat(r => r.For()) + .Should(c => + { + //this check mainly applies to MSMQ who creates a DTC tx right of the bat if DTC is on + Assert.AreEqual(Guid.Empty, c.DistributedIdentifierBefore, "No DTC tx should exist before enlistment"); + Assert.True(c.CanEnlistPromotable, "A promotable RM should be able to enlist"); + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool HandlerInvoked { get; set; } + + public Guid DistributedIdentifierBefore { get; set; } + + public bool CanEnlistPromotable { get; set; } + } + + public class NonDTCEndpoint : EndpointConfigurationBuilder + { + public NonDTCEndpoint() + { + Configure.Transactions.Advanced(a => a.DisableDistributedTransactions()); + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyMessage messageThatIsEnlisted) + { + Context.DistributedIdentifierBefore = Transaction.Current.TransactionInformation.DistributedIdentifier; + + Context.CanEnlistPromotable = Transaction.Current.EnlistPromotableSinglePhase(new FakePromotableResourceManager()); + + Context.HandlerInvoked = true; + } + } + } + + [Serializable] + public class MyMessage : ICommand + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_enabled.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_enabled.cs new file mode 100644 index 000000000..07eac2465 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_dtc_enabled.cs @@ -0,0 +1,83 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_receiving_a_message_with_dtc_enabled : NServiceBusAcceptanceTest + { + [Test] + public void Should_enlist_the_receive_in_the_dtc_tx() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MyMessage()))) + .Done(c => c.HandlerInvoked) + .Repeat(r => r.For()) + .Should(c => Assert.False(c.CanEnlistPromotable, "There should exists a DTC tx")) + .Run(); + } + + [Test] + public void Basic_assumptions_promotable_should_fail_if_durable_already_exists() + { + using (var tx = new TransactionScope()) + { + Transaction.Current.EnlistDurable(FakePromotableResourceManager.ResourceManagerId, new FakePromotableResourceManager(), EnlistmentOptions.None); + Assert.False(Transaction.Current.EnlistPromotableSinglePhase(new FakePromotableResourceManager())); + + tx.Complete(); + } + } + + + [Test] + public void Basic_assumptions_second_promotable_should_fail() + { + using (var tx = new TransactionScope()) + { + Assert.True(Transaction.Current.EnlistPromotableSinglePhase(new FakePromotableResourceManager())); + + Assert.False(Transaction.Current.EnlistPromotableSinglePhase(new FakePromotableResourceManager())); + + tx.Complete(); + } + } + + + public class Context : ScenarioContext + { + public bool HandlerInvoked { get; set; } + + public bool CanEnlistPromotable { get; set; } + } + + public class DTCEndpoint : EndpointConfigurationBuilder + { + public DTCEndpoint() + { + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyMessage messageThatIsEnlisted) + { + Context.CanEnlistPromotable = Transaction.Current.EnlistPromotableSinglePhase(new FakePromotableResourceManager()); + Context.HandlerInvoked = true; + } + } + } + + [Serializable] + public class MyMessage : ICommand + { + } + + + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_the_default_settings.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_the_default_settings.cs new file mode 100644 index 000000000..3ab7159fe --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_the_default_settings.cs @@ -0,0 +1,53 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_receiving_a_message_with_the_default_settings : NServiceBusAcceptanceTest + { + [Test] + public void Should_wrap_the_handler_pipeline_with_a_transactionscope() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MyMessage()))) + .Done(c => c.HandlerInvoked) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.True(c.AmbientTransactionExists, "There should exist an ambient transaction")) + .Run(); + } + + public class Context : ScenarioContext + { + public bool AmbientTransactionExists { get; set; } + public bool HandlerInvoked { get; set; } + } + + public class TransactionalEndpoint : EndpointConfigurationBuilder + { + public TransactionalEndpoint() + { + EndpointSetup(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MyMessage messageThatIsEnlisted) + { + Context.AmbientTransactionExists = (Transaction.Current != null); + Context.HandlerInvoked = true; + } + } + } + + [Serializable] + public class MyMessage : ICommand + { + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_transactions_disabled.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_transactions_disabled.cs new file mode 100644 index 000000000..fe18fb188 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_receiving_a_message_with_transactions_disabled.cs @@ -0,0 +1,80 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_receiving_a_message_with_transactions_disabled : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_roll_the_message_back_to_the_queue_in_case_of_failure() + { + + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MyMessage()))) + .Done(c => c.TestComplete) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.AreEqual(1, c.TimesCalled, "Should not retry the message")) + .Run(); + } + + public class Context : ScenarioContext + { + public bool TestComplete { get; set; } + + public int TimesCalled { get; set; } + } + + public class NonTransactionalEndpoint : EndpointConfigurationBuilder + { + public NonTransactionalEndpoint() + { + EndpointSetup(c => Configure.Transactions.Disable()) + .AllowExceptions(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + public void Handle(MyMessage message) + { + Context.TimesCalled++; + + using (new TransactionScope(TransactionScopeOption.Suppress)) + { + Bus.SendLocal(new CompleteTest()); + } + + throw new Exception("Simulated exception"); + } + } + + public class CompleteTestHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(CompleteTest message) + { + Context.TestComplete = true; + } + } + } + + [Serializable] + public class MyMessage : ICommand + { + } + + [Serializable] + public class CompleteTest : ICommand + { + } + + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_a_message_from_a_non_transactional_endpoint_with_a_ambient_transaction_enabled.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_a_message_from_a_non_transactional_endpoint_with_a_ambient_transaction_enabled.cs new file mode 100644 index 000000000..817161229 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_a_message_from_a_non_transactional_endpoint_with_a_ambient_transaction_enabled.cs @@ -0,0 +1,91 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using AcceptanceTesting; + using EndpointTemplates; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_a_message_from_a_non_transactional_endpoint_with_a_ambient_transaction_enabled : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_roll_the_message_back_to_the_queue_in_case_of_failure() + { + + Scenario.Define() + .WithEndpoint(b => b.Given(bus => bus.SendLocal(new MyMessage()))) + .Done(c => c.TestComplete) + .Repeat(r => r.For()) + .Should(c => Assert.False(c.MessageEnlistedInTheAmbientTxReceived, "The enlisted bus.Send should not commit")) + .Run(); + } + + public class Context : ScenarioContext + { + public bool TestComplete { get; set; } + + public bool MessageEnlistedInTheAmbientTxReceived { get; set; } + } + + public class NonTransactionalEndpoint : EndpointConfigurationBuilder + { + public NonTransactionalEndpoint() + { + EndpointSetup(c => + { + Configure.Transactions.Disable(); + Configure.Transactions.Advanced(t => t.WrapHandlersExecutionInATransactionScope()); + }) + .AllowExceptions(); + } + + public class MyMessageHandler : IHandleMessages + { + public Context Context { get; set; } + + public IBus Bus { get; set; } + public void Handle(MyMessage message) + { + Bus.SendLocal(new CompleteTest + { + EnlistedInTheAmbientTx = true + }); + + using (new TransactionScope(TransactionScopeOption.Suppress)) + { + Bus.SendLocal(new CompleteTest()); + } + + throw new Exception("Simulated exception"); + } + } + + public class CompleteTestHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(CompleteTest message) + { + if (!Context.MessageEnlistedInTheAmbientTxReceived) + Context.MessageEnlistedInTheAmbientTxReceived = message.EnlistedInTheAmbientTx; + + Context.TestComplete = true; + } + } + } + + [Serializable] + public class MyMessage : ICommand + { + } + + [Serializable] + public class CompleteTest : ICommand + { + public bool EnlistedInTheAmbientTx { get; set; } + } + + + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_messages_within_an_ambient_transaction.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_messages_within_an_ambient_transaction.cs new file mode 100644 index 000000000..377ffd919 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Transactions/When_sending_messages_within_an_ambient_transaction.cs @@ -0,0 +1,122 @@ +namespace NServiceBus.AcceptanceTests.Transactions +{ + using System; + using System.Transactions; + using EndpointTemplates; + using AcceptanceTesting; + using NUnit.Framework; + using ScenarioDescriptors; + + public class When_sending_messages_within_an_ambient_transaction : NServiceBusAcceptanceTest + { + [Test] + public void Should_not_deliver_them_until_the_commit_phase() + { + Scenario.Define() + .WithEndpoint(b => b.Given((bus, context) => + { + using (var tx = new TransactionScope()) + { + bus.Send(new MessageThatIsEnlisted { SequenceNumber = 1 }); + bus.Send(new MessageThatIsEnlisted { SequenceNumber = 2 }); + + //send another message as well so that we can check the order in the receiver + using (new TransactionScope(TransactionScopeOption.Suppress)) + { + bus.Send(new MessageThatIsNotEnlisted()); + } + + tx.Complete(); + } + })) + .Done(c => c.MessageThatIsNotEnlistedHandlerWasCalled && c.TimesCalled >= 2) + .Repeat(r => r.For(Transports.Default)) //not stable for sqlserver + .Should(c => Assert.AreEqual(1, c.SequenceNumberOfFirstMessage,"The transport should preserve the order in which the transactional messages are delivered to the queuing system")) + .Run(); + } + + [Test] + public void Should_not_deliver_them_on_rollback() + { + Scenario.Define() + .WithEndpoint(b => b.Given(bus => + { + using (new TransactionScope()) + { + bus.Send(new MessageThatIsEnlisted()); + + //rollback + } + + bus.Send(new MessageThatIsNotEnlisted()); + + })) + .Done(c => c.MessageThatIsNotEnlistedHandlerWasCalled) + .Repeat(r => r.For(Transports.Default)) + .Should(c => Assert.False(c.MessageThatIsEnlistedHandlerWasCalled, "The transactional handler should not be called")) + .Run(); + } + + public class Context : ScenarioContext + { + public bool MessageThatIsEnlistedHandlerWasCalled { get; set; } + + public bool MessageThatIsNotEnlistedHandlerWasCalled { get; set; } + public int TimesCalled { get; set; } + + public int SequenceNumberOfFirstMessage { get; set; } + + public bool NonTransactionalHandlerCalledFirst { get; set; } + } + + public class TransactionalEndpoint : EndpointConfigurationBuilder + { + public TransactionalEndpoint() + { + EndpointSetup() + .AddMapping(typeof(TransactionalEndpoint)) + .AddMapping(typeof(TransactionalEndpoint)); + } + + public class MessageThatIsEnlistedHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageThatIsEnlisted messageThatIsEnlisted) + { + Context.MessageThatIsEnlistedHandlerWasCalled = true; + Context.TimesCalled++; + + if (Context.SequenceNumberOfFirstMessage == 0) + { + Context.SequenceNumberOfFirstMessage = messageThatIsEnlisted.SequenceNumber; + } + } + } + + public class MessageThatIsNotEnlistedHandler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(MessageThatIsNotEnlisted messageThatIsNotEnlisted) + { + Context.MessageThatIsNotEnlistedHandlerWasCalled = true; + Context.NonTransactionalHandlerCalledFirst = !Context.MessageThatIsEnlistedHandlerWasCalled; + } + } + } + + + [Serializable] + public class MessageThatIsEnlisted : ICommand + { + public int SequenceNumber { get; set; } + } + [Serializable] + public class MessageThatIsNotEnlisted : ICommand + { + } + + + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/Versioning/When_multiple_versions_of_a_message_is_published.cs b/src/NServiceBus.SqlServer.AcceptanceTests/Versioning/When_multiple_versions_of_a_message_is_published.cs new file mode 100644 index 000000000..e19464646 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/Versioning/When_multiple_versions_of_a_message_is_published.cs @@ -0,0 +1,124 @@ +namespace NServiceBus.AcceptanceTests.Versioning +{ + using EndpointTemplates; + using AcceptanceTesting; + using Features; + using NUnit.Framework; + using PubSub; + using ScenarioDescriptors; + + public class When_multiple_versions_of_a_message_is_published : NServiceBusAcceptanceTest + { + [Test] + public void Should_deliver_is_to_both_v1_and_vX_subscribers() + { + Scenario.Define() + .WithEndpoint(b => + b.Given((bus, context) => Subscriptions.OnEndpointSubscribed(s => + { + if (s.SubscriberReturnAddress.Queue.Contains("V1Subscriber")) + context.V1Subscribed = true; + + if (s.SubscriberReturnAddress.Queue.Contains("V2Subscriber")) + context.V2Subscribed = true; + }) + ) + .When(c => c.V1Subscribed && c.V2Subscribed, (bus, c) => bus.Publish(e => + { + e.SomeData = 1; + e.MoreInfo = "dasd"; + }))) + .WithEndpoint(b => b.Given((bus,c) => + { + bus.Subscribe(); + if (!Feature.IsEnabled()) + c.V1Subscribed = true; + })) + .WithEndpoint(b => b.Given((bus,c) => + { + bus.Subscribe(); + if (!Feature.IsEnabled()) + c.V2Subscribed = true; + })) + .Done(c => c.V1SubscriberGotTheMessage && c.V2SubscriberGotTheMessage) + .Repeat(r =>//broken for active mq until #1098 is fixed + r.For(Serializers.Binary)) //versioning isn't supported for binary serialization + .Should(c => + { + //put asserts in here if needed + }) + .Run(); + } + + public class Context : ScenarioContext + { + public bool V1SubscriberGotTheMessage { get; set; } + + public bool V2SubscriberGotTheMessage { get; set; } + + public bool V1Subscribed { get; set; } + + public bool V2Subscribed { get; set; } + } + + public class V2Publisher : EndpointConfigurationBuilder + { + public V2Publisher() + { + EndpointSetup(); + + } + } + public class V1Subscriber : EndpointConfigurationBuilder + { + public V1Subscriber() + { + EndpointSetup() + .ExcludeType() + .AddMapping(typeof(V2Publisher)); + + } + + + class V1Handler:IHandleMessages + { + public Context Context { get; set; } + public void Handle(V1Event message) + { + Context.V1SubscriberGotTheMessage = true; + } + } + } + + + public class V2Subscriber : EndpointConfigurationBuilder + { + public V2Subscriber() + { + EndpointSetup() + .AddMapping(typeof(V2Publisher)); + } + + class V2Handler : IHandleMessages + { + public Context Context { get; set; } + + public void Handle(V2Event message) + { + Context.V2SubscriberGotTheMessage = true; + } + } + } + + + public interface V1Event : IEvent + { + int SomeData { get; set; } + } + + public interface V2Event : V1Event + { + string MoreInfo { get; set; } + } + } +} diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/packages.config b/src/NServiceBus.SqlServer.AcceptanceTests/packages.config new file mode 100644 index 000000000..f2991b3b1 --- /dev/null +++ b/src/NServiceBus.SqlServer.AcceptanceTests/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.AcceptanceTests/ripple.dependencies.config b/src/NServiceBus.SqlServer.AcceptanceTests/ripple.dependencies.config deleted file mode 100644 index 4397c0e7e..000000000 --- a/src/NServiceBus.SqlServer.AcceptanceTests/ripple.dependencies.config +++ /dev/null @@ -1,6 +0,0 @@ -log4net -NUnit -NServiceBus -NServiceBus.Interfaces -NServiceBus.AcceptanceTesting -NServiceBus.AcceptanceTests.Sources \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.UnitTests/ConnectionStringParserTests.cs b/src/NServiceBus.SqlServer.UnitTests/ConnectionStringParserTests.cs new file mode 100644 index 000000000..339dd5aba --- /dev/null +++ b/src/NServiceBus.SqlServer.UnitTests/ConnectionStringParserTests.cs @@ -0,0 +1,42 @@ +namespace NServiceBus.SqlServer.UnitTests +{ + using NServiceBus.Transports.SQLServer; + using NUnit.Framework; + + [TestFixture] + public class ConnectionStringParserTests + { + [Test] + public void Should_extract_expected_schema_name_from_connection_string() + { + const string expected = "test_schema"; + const string test_connection_string = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True; Queue Schema=test_schema"; + + var info = ConnectionStringParser.AsConnectionInfo(test_connection_string); + + Assert.AreEqual(expected, info.SchemaName); + } + + [Test] + public void Schema_name_shopuld_default_to_dbo_if_not_specified() + { + const string expected = "dbo"; + const string test_connection_string = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True;"; + + var info = ConnectionStringParser.AsConnectionInfo( test_connection_string ); + + Assert.AreEqual( expected, info.SchemaName ); + } + + [Test] + public void Should_extract_expected_connection_string() + { + const string expected = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True"; + const string test_connection_string = @"Data Source=.\SQLEXPRESS;Initial Catalog=NServiceBus;Integrated Security=True; Queue Schema=test_schema"; + + var info = ConnectionStringParser.AsConnectionInfo( test_connection_string ); + + Assert.AreEqual( expected.ToLowerInvariant(), info.ConnectionString.ToLowerInvariant() ); + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.UnitTests/NServiceBus.SqlServer.UnitTests.csproj b/src/NServiceBus.SqlServer.UnitTests/NServiceBus.SqlServer.UnitTests.csproj new file mode 100644 index 000000000..02e11408a --- /dev/null +++ b/src/NServiceBus.SqlServer.UnitTests/NServiceBus.SqlServer.UnitTests.csproj @@ -0,0 +1,85 @@ + + + + + Debug + AnyCPU + {0B1B5564-1941-439C-A1CF-2703BD722999} + Library + Properties + NServiceBus.SqlServer.UnitTests + NServiceBus.SqlServer.UnitTests + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + NServiceBus.snk + + + + ..\packages\NServiceBus.Interfaces.4.7.5\lib\net40\NServiceBus.dll + True + + + ..\packages\NServiceBus.4.7.5\lib\net40\NServiceBus.Core.dll + True + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + + + + + + + + + + + + + + + + {FA1193BF-325C-4201-BB78-484032E09809} + NServiceBus.SqlServer + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.UnitTests/NServiceBus.snk b/src/NServiceBus.SqlServer.UnitTests/NServiceBus.snk new file mode 100644 index 000000000..6fa7ddec1 Binary files /dev/null and b/src/NServiceBus.SqlServer.UnitTests/NServiceBus.snk differ diff --git a/src/NServiceBus.SqlServer.UnitTests/Properties/AssemblyInfo.cs b/src/NServiceBus.SqlServer.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..3ac748cae --- /dev/null +++ b/src/NServiceBus.SqlServer.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NServiceBus.SqlServer.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NServiceBus.SqlServer.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8db0f1ee-627f-4470-98d5-b9ef0b67e3f8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/NServiceBus.SqlServer.UnitTests/packages.config b/src/NServiceBus.SqlServer.UnitTests/packages.config new file mode 100644 index 000000000..bd9e5307b --- /dev/null +++ b/src/NServiceBus.SqlServer.UnitTests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer.sln b/src/NServiceBus.SqlServer.sln index 11f64dd5c..f9e66113c 100644 --- a/src/NServiceBus.SqlServer.sln +++ b/src/NServiceBus.SqlServer.sln @@ -1,10 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBus.SqlServer", "NServiceBus.SqlServer\NServiceBus.SqlServer.csproj", "{FA1193BF-325C-4201-BB78-484032E09809}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBus.SqlServer.AcceptanceTests", "NServiceBus.SqlServer.AcceptanceTests\NServiceBus.SqlServer.AcceptanceTests.csproj", "{6A9E04E7-6229-4A3E-B94A-DA168E962B5A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FDEEABC1-953C-4DC5-8C36-815714860D7D}" + ProjectSection(SolutionItems) = preProject + ..\packaging\nuget\NServiceBus.SqlServer.nuspec = ..\packaging\nuget\NServiceBus.SqlServer.nuspec + Scripts\Reset-Database.sql = Scripts\Reset-Database.sql + Scripts\ReturnToSourceQueue.sql = Scripts\ReturnToSourceQueue.sql + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBus.SqlServer.UnitTests", "NServiceBus.SqlServer.UnitTests\NServiceBus.SqlServer.UnitTests.csproj", "{0B1B5564-1941-439C-A1CF-2703BD722999}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,6 +30,10 @@ Global {6A9E04E7-6229-4A3E-B94A-DA168E962B5A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A9E04E7-6229-4A3E-B94A-DA168E962B5A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A9E04E7-6229-4A3E-B94A-DA168E962B5A}.Release|Any CPU.Build.0 = Release|Any CPU + {0B1B5564-1941-439C-A1CF-2703BD722999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B1B5564-1941-439C-A1CF-2703BD722999}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B1B5564-1941-439C-A1CF-2703BD722999}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B1B5564-1941-439C-A1CF-2703BD722999}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/NServiceBus.SqlServer.sln.DotSettings b/src/NServiceBus.SqlServer.sln.DotSettings index 1a34c021d..2aeba8226 100644 --- a/src/NServiceBus.SqlServer.sln.DotSettings +++ b/src/NServiceBus.SqlServer.sln.DotSettings @@ -14,9 +14,24 @@ DO_NOT_SHOW ERROR ERROR - WARNING + ERROR ERROR + ERROR + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + ERROR ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR ERROR ERROR ERROR @@ -24,13 +39,15 @@ ERROR ERROR ERROR + ERROR ERROR DO_NOT_SHOW DO_NOT_SHOW + ERROR DO_NOT_SHOW DO_NOT_SHOW ERROR - WARNING + ERROR ERROR ERROR ERROR @@ -44,8 +61,10 @@ ERROR ERROR ERROR - SUGGESTION - DO_NOT_SHOW + ERROR + ERROR + SUGGESTION + SUGGESTION ERROR ERROR ERROR @@ -61,6 +80,7 @@ ERROR ERROR ERROR + ERROR ERROR ERROR WARNING @@ -86,25 +106,41 @@ DoHide DoHide DoHide + ERROR ERROR ERROR ERROR + ERROR ERROR ERROR ERROR ERROR + ERROR ERROR ERROR DO_NOT_SHOW SUGGESTION + WARNING + WARNING ERROR ERROR ERROR ERROR ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR + ERROR <?xml version="1.0" encoding="utf-16"?><Profile name="Format My Code Using &quot;Particular&quot; conventions"><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><JsReformatCode>True</JsReformatCode><CssReformatCode>True</CssReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><HtmlReformatCode>True</HtmlReformatCode><CSShortenReferences>True</CSShortenReferences><CSharpFormatDocComments>True</CSharpFormatDocComments><CssAlphabetizeProperties>True</CssAlphabetizeProperties></Profile> Default: Reformat Code Format My Code Using "Particular" conventions + False False ALWAYS_ADD ALWAYS_ADD @@ -120,6 +156,142 @@ CHOP_ALWAYS True True + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <TypePattern DisplayName="COM interfaces or structs"> + <TypePattern.Match> + <Or> + <And> + <Kind Is="Interface" /> + <Or> + <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> + <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> + </Or> + </And> + <HasAttribute Name="System.Runtime.InteropServices.StructLayoutAttribute" /> + </Or> + </TypePattern.Match> + </TypePattern> + <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" /> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry DisplayName="Test Methods" Priority="100"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="Default Pattern"> + <Entry DisplayName="Public Delegates" Priority="100"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Delegate" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public Enums" Priority="100"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Enum" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Static Fields and Constants"> + <Entry.Match> + <Or> + <Kind Is="Constant" /> + <And> + <Kind Is="Field" /> + <Static /> + </And> + </Or> + </Entry.Match> + <Entry.SortBy> + <Kind Order="Constant Field" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Constructors"> + <Entry.Match> + <Kind Is="Constructor" /> + </Entry.Match> + <Entry.SortBy> + <Static /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Properties, Indexers"> + <Entry.Match> + <Or> + <Kind Is="Property" /> + <Kind Is="Indexer" /> + </Or> + </Entry.Match> + </Entry> + <Entry DisplayName="Interface Implementations" Priority="100"> + <Entry.Match> + <And> + <Kind Is="Member" /> + <ImplementsInterface /> + </And> + </Entry.Match> + <Entry.SortBy> + <ImplementsInterface Immediate="True" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="All other members" /> + <Entry DisplayName="Fields"> + <Entry.Match> + <And> + <Kind Is="Field" /> + <Not> + <Static /> + </Not> + </And> + </Entry.Match> + <Entry.SortBy> + <Readonly /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Nested Types"> + <Entry.Match> + <Kind Is="Type" /> + </Entry.Match> + </Entry> + </TypePattern> +</Patterns> <?xml version="1.0" encoding="utf-8" ?> <!-- @@ -326,6 +498,7 @@ II.2.12 <HandlesEvent /> CustomLayout True + True True False True @@ -383,7 +556,10 @@ II.2.12 <HandlesEvent /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True True + True True diff --git a/src/NServiceBus.SqlServer/ConnectionInfo.cs b/src/NServiceBus.SqlServer/ConnectionInfo.cs new file mode 100644 index 000000000..6a83ec1f1 --- /dev/null +++ b/src/NServiceBus.SqlServer/ConnectionInfo.cs @@ -0,0 +1,19 @@ +namespace NServiceBus.Transports.SQLServer +{ + class ConnectionInfo + { + internal ConnectionInfo( string connectionString, string schemaName ) + { + ConnectionString = connectionString; + SchemaName = schemaName; + if( string.IsNullOrWhiteSpace( SchemaName ) ) + { + SchemaName = "dbo"; + } + } + + public string SchemaName { get; private set; } + + public string ConnectionString { get; private set; } + } +} diff --git a/src/NServiceBus.SqlServer/ConnectionStringParser.cs b/src/NServiceBus.SqlServer/ConnectionStringParser.cs new file mode 100644 index 000000000..92898abf9 --- /dev/null +++ b/src/NServiceBus.SqlServer/ConnectionStringParser.cs @@ -0,0 +1,28 @@ +namespace NServiceBus.Transports.SQLServer +{ + using System.Data.Common; + + class ConnectionStringParser + { + public static ConnectionInfo AsConnectionInfo( string connectionString ) + { + const string key = "Queue Schema"; + string _schemaName = null; + var _connectionString = connectionString; + + var connectionStringParser = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if( connectionStringParser.ContainsKey( key ) ) + { + _schemaName = ( string )connectionStringParser[ key ]; + connectionStringParser.Remove( key ); + _connectionString = connectionStringParser.ConnectionString; + } + + return new ConnectionInfo( _connectionString, _schemaName ); + } + } +} diff --git a/src/NServiceBus.SqlServer/Fody.targets b/src/NServiceBus.SqlServer/Fody.targets deleted file mode 100644 index a668a51fc..000000000 --- a/src/NServiceBus.SqlServer/Fody.targets +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - $(NCrunchOriginalSolutionDir) - - - - - $(SolutionDir) - - - - - $(MSBuildProjectDirectory)\..\ - - - - - - - $(KeyOriginatorFile) - - - - - $(AssemblyOriginatorKeyFile) - - - - - - - - - - $(ProjectDir)$(IntermediateOutputPath) - Low - $(SignAssembly) - $(MSBuildThisFileDirectory) - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/FodyWeavers.xml b/src/NServiceBus.SqlServer/FodyWeavers.xml index 36b1bd239..7172ab76b 100644 --- a/src/NServiceBus.SqlServer/FodyWeavers.xml +++ b/src/NServiceBus.SqlServer/FodyWeavers.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/GitVersionConfig.yaml b/src/NServiceBus.SqlServer/GitVersionConfig.yaml new file mode 100644 index 000000000..10b5dc97c --- /dev/null +++ b/src/NServiceBus.SqlServer/GitVersionConfig.yaml @@ -0,0 +1 @@ +assembly-versioning-scheme: Major \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/NServiceBus.SqlServer.csproj b/src/NServiceBus.SqlServer/NServiceBus.SqlServer.csproj index 3a237b494..e1fdd968b 100644 --- a/src/NServiceBus.SqlServer/NServiceBus.SqlServer.csproj +++ b/src/NServiceBus.SqlServer/NServiceBus.SqlServer.csproj @@ -13,7 +13,7 @@ 512 true $(SolutionDir)NServiceBus.snk - ..\packages\Fody + a65d899d true @@ -37,15 +37,18 @@ 1591,1572,1571,1573,1587,1570 - - ..\packages\Janitor.Fody\Lib\portable-net4+sl4+wp7+win8+MonoAndroid16+MonoTouch40\Janitor.dll + + ..\packages\Janitor.Fody.1.1.1.0\Lib\portable-net4+sl5+wp8+win8+wpa81+MonoAndroid16+MonoTouch40\Janitor.dll + False - - ..\packages\NServiceBus.Interfaces\lib\net40\NServiceBus.dll + + ..\packages\NServiceBus.Interfaces.4.7.5\lib\net40\NServiceBus.dll + True - - ..\packages\NServiceBus\lib\net40\NServiceBus.Core.dll + + ..\packages\NServiceBus.4.7.5\lib\net40\NServiceBus.Core.dll + True @@ -58,6 +61,8 @@ + + @@ -73,14 +78,18 @@ - - - Designer - + - - - - + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/Properties/AssemblyInfo.cs b/src/NServiceBus.SqlServer/Properties/AssemblyInfo.cs index 6f66b21b6..41a1c111c 100644 --- a/src/NServiceBus.SqlServer/Properties/AssemblyInfo.cs +++ b/src/NServiceBus.SqlServer/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("NServiceBus.SQL")] @@ -11,3 +12,4 @@ [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo( "NServiceBus.SqlServer.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100dde965e6172e019ac82c2639ffe494dd2e7dd16347c34762a05732b492e110f2e4e2e1b5ef2d85c848ccfb671ee20a47c8d1376276708dc30a90ff1121b647ba3b7259a6bc383b2034938ef0e275b58b920375ac605076178123693c6c4f1331661a62eba28c249386855637780e3ff5f23a6d854700eaa6803ef48907513b92" )] \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/SqlServerMessageSender.cs b/src/NServiceBus.SqlServer/SqlServerMessageSender.cs index c5eba3d1e..5156dff94 100644 --- a/src/NServiceBus.SqlServer/SqlServerMessageSender.cs +++ b/src/NServiceBus.SqlServer/SqlServerMessageSender.cs @@ -14,15 +14,19 @@ public class SqlServerMessageSender : ISendMessages { const string SqlSend = - @"INSERT INTO [{0}] ([Id],[CorrelationId],[ReplyToAddress],[Recoverable],[Expires],[Headers],[Body]) + @"INSERT INTO [{0}].[{1}] ([Id],[CorrelationId],[ReplyToAddress],[Recoverable],[Expires],[Headers],[Body]) VALUES (@Id,@CorrelationId,@ReplyToAddress,@Recoverable,@Expires,@Headers,@Body)"; static JsonMessageSerializer Serializer = new JsonMessageSerializer(null); public string DefaultConnectionString { get; set; } + public string DefaultSchemaName { get; set; } + public Dictionary ConnectionStringCollection { get; set; } + public Dictionary SchemaNameCollection { get; set; } + public UnitOfWork UnitOfWork { get; set; } public SqlServerMessageSender() @@ -45,17 +49,24 @@ public void Send(TransportMessage message, Address address) { //If there is a connectionstring configured for the queue, use that connectionstring var queueConnectionString = DefaultConnectionString; + var schemaName = DefaultSchemaName; + if (ConnectionStringCollection.Keys.Contains(address.Queue)) { queueConnectionString = ConnectionStringCollection[address.Queue]; } + if( SchemaNameCollection.Keys.Contains( address.Queue ) ) + { + schemaName = SchemaNameCollection[ address.Queue ]; + } + if (UnitOfWork.HasActiveTransaction(queueConnectionString)) { //if there is an active transaction for the connection, we can use the same native transaction var transaction = UnitOfWork.GetTransaction(queueConnectionString); - using (var command = new SqlCommand(string.Format(SqlSend, TableNameUtils.GetTableName(address)), transaction.Connection, transaction) + using (var command = new SqlCommand(string.Format(SqlSend, schemaName, TableNameUtils.GetTableName(address)), transaction.Connection, transaction) { CommandType = CommandType.Text }) @@ -69,7 +80,7 @@ public void Send(TransportMessage message, Address address) using (var connection = new SqlConnection(queueConnectionString)) { connection.Open(); - using (var command = new SqlCommand(string.Format(SqlSend, TableNameUtils.GetTableName(address)), connection) + using (var command = new SqlCommand(string.Format(SqlSend, schemaName, TableNameUtils.GetTableName(address)), connection) { CommandType = CommandType.Text }) diff --git a/src/NServiceBus.SqlServer/SqlServerPollingDequeueStrategy.cs b/src/NServiceBus.SqlServer/SqlServerPollingDequeueStrategy.cs index ee07924c8..1e44ea629 100644 --- a/src/NServiceBus.SqlServer/SqlServerPollingDequeueStrategy.cs +++ b/src/NServiceBus.SqlServer/SqlServerPollingDequeueStrategy.cs @@ -23,6 +23,11 @@ public class SqlServerPollingDequeueStrategy : IDequeueMessages, IDisposable /// public string ConnectionString { get; set; } + /// + /// The name of the schema to use when connection to SQL Server. + /// + public string SchemaName { get; set; } + /// /// Determines if the queue should be purged when the transport starts. /// @@ -60,7 +65,7 @@ public void Init(Address address, TransactionSettings transactionSettings, tableName = TableNameUtils.GetTableName(address); - sql = string.Format(SqlReceive, tableName); + sql = string.Format(SqlReceive, SchemaName, tableName); if (PurgeOnStartup) { @@ -105,7 +110,7 @@ void PurgeTable() { connection.Open(); - using (var command = new SqlCommand(string.Format(SqlPurge, tableName), connection) + using (var command = new SqlCommand(string.Format(SqlPurge, SchemaName, tableName), connection) { CommandType = CommandType.Text }) @@ -400,17 +405,17 @@ IsolationLevel GetSqlIsolationLevel(System.Transactions.IsolationLevel isolation } const string SqlReceive = - @"WITH message AS (SELECT TOP(1) * FROM [{0}] WITH (UPDLOCK, READPAST, ROWLOCK) ORDER BY [RowVersion] ASC) + @"WITH message AS (SELECT TOP(1) * FROM [{0}].[{1}] WITH (UPDLOCK, READPAST, ROWLOCK) ORDER BY [RowVersion] ASC) DELETE FROM message OUTPUT deleted.Id, deleted.CorrelationId, deleted.ReplyToAddress, deleted.Recoverable, deleted.Expires, deleted.Headers, deleted.Body;"; - const string SqlPurge = @"DELETE FROM [{0}]"; + const string SqlPurge = @"DELETE FROM [{0}].[{1}]"; static readonly JsonMessageSerializer Serializer = new JsonMessageSerializer(null); static readonly ILog Logger = LogManager.GetLogger(typeof(SqlServerPollingDequeueStrategy)); - readonly RepeatedFailuresOverTimeCircuitBreaker circuitBreaker = new RepeatedFailuresOverTimeCircuitBreaker("SqlTransportConnectivity", + RepeatedFailuresOverTimeCircuitBreaker circuitBreaker = new RepeatedFailuresOverTimeCircuitBreaker("SqlTransportConnectivity", TimeSpan.FromMinutes(2), ex => Configure.Instance.RaiseCriticalError("Repeated failures when communicating with SqlServer", ex), TimeSpan.FromSeconds(10)); diff --git a/src/NServiceBus.SqlServer/SqlServerQueueCreator.cs b/src/NServiceBus.SqlServer/SqlServerQueueCreator.cs index 4ab180460..73d893ce9 100644 --- a/src/NServiceBus.SqlServer/SqlServerQueueCreator.cs +++ b/src/NServiceBus.SqlServer/SqlServerQueueCreator.cs @@ -6,9 +6,9 @@ namespace NServiceBus.Transports.SQLServer public class SqlServerQueueCreator : ICreateQueues { const string Ddl = - @"IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[{0}]') AND type in (N'U')) + @"IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[{0}].[{1}]') AND type in (N'U')) BEGIN - CREATE TABLE [dbo].[{0}]( + CREATE TABLE [{0}].[{1}]( [Id] [uniqueidentifier] NOT NULL, [CorrelationId] [varchar](255) NULL, [ReplyToAddress] [varchar](255) NULL, @@ -19,7 +19,7 @@ [Body] [varbinary](max) NULL, [RowVersion] [bigint] IDENTITY(1,1) NOT NULL ) ON [PRIMARY]; - CREATE CLUSTERED INDEX [Index_RowVersion] ON [dbo].[{0}] + CREATE CLUSTERED INDEX [Index_RowVersion] ON [{0}].[{1}] ( [RowVersion] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] @@ -30,7 +30,7 @@ public void CreateQueueIfNecessary(Address address, string account) { using (var connection = new SqlConnection(ConnectionString)) { - var sql = string.Format(Ddl, TableNameUtils.GetTableName(address)); + var sql = string.Format(Ddl, SchemaName, TableNameUtils.GetTableName(address)); connection.Open(); using (var command = new SqlCommand(sql, connection) {CommandType = CommandType.Text}) @@ -41,5 +41,7 @@ public void CreateQueueIfNecessary(Address address, string account) } public string ConnectionString { get; set; } + + public string SchemaName { get; set; } } } \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/SqlServerTransport.cs b/src/NServiceBus.SqlServer/SqlServerTransport.cs index 4d5fd9fa7..a9460ecfb 100644 --- a/src/NServiceBus.SqlServer/SqlServerTransport.cs +++ b/src/NServiceBus.SqlServer/SqlServerTransport.cs @@ -28,33 +28,54 @@ public override void Initialize() { //Until we refactor the whole address system CustomizeAddress(); - - var defaultConnectionString = SettingsHolder.Get("NServiceBus.Transport.ConnectionString"); + + var connectionStringSetting = SettingsHolder.Get("NServiceBus.Transport.ConnectionString"); + if (string.IsNullOrEmpty(connectionStringSetting)) + { + throw new ArgumentException("Sql Transport connection string cannot be empty or null."); + } + + var defaultConnectionInfo = ConnectionStringParser.AsConnectionInfo(connectionStringSetting); //Load all connectionstrings - var collection = + var connectionStringCollection = ConfigurationManager .ConnectionStrings .Cast() .Where(x => x.Name.StartsWith("NServiceBus/Transport/")) - .ToDictionary(x => x.Name.Replace("NServiceBus/Transport/", String.Empty), y => y.ConnectionString); + .ToDictionary(x => x.Name.Replace("NServiceBus/Transport/", string.Empty), y => + { + var info= ConnectionStringParser.AsConnectionInfo(y.ConnectionString); + return info.ConnectionString; + }); - if (String.IsNullOrEmpty(defaultConnectionString)) - { - throw new ArgumentException("Sql Transport connection string cannot be empty or null."); - } + var schemaNameCollection = + ConfigurationManager + .ConnectionStrings + .Cast() + .Where(x => x.Name.StartsWith("NServiceBus/Transport/")) + .ToDictionary(x => x.Name.Replace("NServiceBus/Transport/", string.Empty), y => + { + var info= ConnectionStringParser.AsConnectionInfo(y.ConnectionString); + return info.SchemaName; + }); - NServiceBus.Configure.Component(DependencyLifecycle.SingleInstance); + NServiceBus.Configure.Component(DependencyLifecycle.SingleInstance) + .ConfigureProperty( p => p.DefaultConnectionString, defaultConnectionInfo.ConnectionString ); NServiceBus.Configure.Component(DependencyLifecycle.InstancePerCall) - .ConfigureProperty(p => p.ConnectionString, defaultConnectionString); + .ConfigureProperty(p => p.ConnectionString, defaultConnectionInfo.ConnectionString) + .ConfigureProperty( p => p.SchemaName, defaultConnectionInfo.SchemaName); NServiceBus.Configure.Component(DependencyLifecycle.InstancePerCall) - .ConfigureProperty(p => p.DefaultConnectionString, defaultConnectionString) - .ConfigureProperty(p => p.ConnectionStringCollection, collection); + .ConfigureProperty(p => p.DefaultConnectionString, defaultConnectionInfo.ConnectionString) + .ConfigureProperty( p => p.DefaultSchemaName, defaultConnectionInfo.SchemaName) + .ConfigureProperty(p => p.ConnectionStringCollection, connectionStringCollection) + .ConfigureProperty( p => p.SchemaNameCollection, schemaNameCollection); NServiceBus.Configure.Component(DependencyLifecycle.InstancePerCall) - .ConfigureProperty(p => p.ConnectionString, defaultConnectionString) + .ConfigureProperty(p => p.ConnectionString, defaultConnectionInfo.ConnectionString) + .ConfigureProperty(p => p.SchemaName, defaultConnectionInfo.SchemaName) .ConfigureProperty(p => p.PurgeOnStartup, ConfigurePurging.PurgeRequested); } diff --git a/src/NServiceBus.SqlServer/UnitOfWork.cs b/src/NServiceBus.SqlServer/UnitOfWork.cs index 6e25add04..94dc10ee7 100644 --- a/src/NServiceBus.SqlServer/UnitOfWork.cs +++ b/src/NServiceBus.SqlServer/UnitOfWork.cs @@ -4,18 +4,12 @@ using System.Collections.Generic; using System.Data.SqlClient; using System.Threading; - using Settings; public class UnitOfWork : IDisposable { - public UnitOfWork() - { - defaultConnectionString = SettingsHolder.Get("NServiceBus.Transport.ConnectionString"); - } - public SqlTransaction Transaction { - get { return GetTransaction(defaultConnectionString); } + get { return GetTransaction(DefaultConnectionString); } } public void Dispose() @@ -30,7 +24,7 @@ public SqlTransaction GetTransaction(string connectionString) public void SetTransaction(SqlTransaction transaction) { - SetTransaction(transaction, defaultConnectionString); + SetTransaction(transaction, DefaultConnectionString); } public void SetTransaction(SqlTransaction transaction, string connectionString) @@ -45,7 +39,7 @@ public void SetTransaction(SqlTransaction transaction, string connectionString) public bool HasActiveTransaction() { - return HasActiveTransaction(defaultConnectionString); + return HasActiveTransaction(DefaultConnectionString); } public bool HasActiveTransaction(string connectionString) @@ -55,7 +49,7 @@ public bool HasActiveTransaction(string connectionString) public void ClearTransaction() { - ClearTransaction(defaultConnectionString); + ClearTransaction(DefaultConnectionString); } public void ClearTransaction(string connectionString) @@ -63,9 +57,9 @@ public void ClearTransaction(string connectionString) currentTransactions.Value.Remove(connectionString); } - readonly ThreadLocal> currentTransactions + ThreadLocal> currentTransactions = new ThreadLocal>(() => new Dictionary(StringComparer.InvariantCultureIgnoreCase)); - string defaultConnectionString; + public string DefaultConnectionString { get; set; } } } \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/packages.config b/src/NServiceBus.SqlServer/packages.config new file mode 100644 index 000000000..bb0c9e1a2 --- /dev/null +++ b/src/NServiceBus.SqlServer/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/NServiceBus.SqlServer/ripple.dependencies.config b/src/NServiceBus.SqlServer/ripple.dependencies.config deleted file mode 100644 index 59aaac0d4..000000000 --- a/src/NServiceBus.SqlServer/ripple.dependencies.config +++ /dev/null @@ -1,2 +0,0 @@ -NServiceBus -NServiceBus.Interfaces \ No newline at end of file diff --git a/src/Scripts/Reset-Database.sql b/src/Scripts/Reset-Database.sql index dd9451aed..ee853c1c2 100644 --- a/src/Scripts/Reset-Database.sql +++ b/src/Scripts/Reset-Database.sql @@ -98,4 +98,23 @@ BEGIN PRINT 'Dropped Table: ' + @name SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > @name ORDER BY [name]) END +GO + +/* Create schemas */ +IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'nsb') +BEGIN + EXEC('CREATE SCHEMA nsb') +END +GO + +IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'sender') +BEGIN + EXEC('CREATE SCHEMA sender') +END +GO + +IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'receiver') +BEGIN + EXEC('CREATE SCHEMA receiver') +END GO \ No newline at end of file diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 000000000..9f16f776e --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file