From 050bb2b7cc7a40b8f18927ef9c743d1d8777d473 Mon Sep 17 00:00:00 2001 From: Brian Ingenito <28159742+bingenito@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:23:39 -0500 Subject: [PATCH] Move from MorganStanley.Fdc3 to Finos.Fdc3 (#464) --- .../tests/DesktopAgent.Tests/EndToEndTests.cs | 986 ++++++++++++ ...3DesktopAgentMessageRouterService.Tests.cs | 1430 +++++++++++++++++ 2 files changed, 2416 insertions(+) create mode 100644 src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/EndToEndTests.cs create mode 100644 src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs diff --git a/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/EndToEndTests.cs b/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/EndToEndTests.cs new file mode 100644 index 000000000..d64c8db65 --- /dev/null +++ b/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/EndToEndTests.cs @@ -0,0 +1,986 @@ +/* + * Morgan Stanley makes this available to you under the Apache License, + * Version 2.0 (the "License"). You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Unless required by applicable law or agreed + * to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + + +using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Contracts; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Converters; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Exceptions; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests.Helpers; +using MorganStanley.ComposeUI.Messaging.Client.WebSocket; +using MorganStanley.ComposeUI.ModuleLoader; +using Finos.Fdc3; +using Finos.Fdc3.Context; +using AppMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppMetadata; +using AppIntent = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIntent; +using AppIdentifier = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIdentifier; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal; + +namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests +{ + public class EndToEndTests : IAsyncLifetime + { + private IHost _host; + private IMessageRouter _messageRouter; + private ServiceProvider _clientServices; + private readonly Uri _webSocketUri = new("ws://localhost:7098/ws"); + private const string TestChannel = "testChannel"; + private readonly UserChannelTopics _topics = new UserChannelTopics(TestChannel); + private const string AccessToken = "token"; + private JsonSerializerOptions _options; + private IModuleLoader _moduleLoader; + private readonly object _runningAppsLock = new(); + private IDisposable _runningAppsObserver; + private readonly List _runningApps = new(); + + public async Task InitializeAsync() + { + // Create the backend side + IHostBuilder builder = new HostBuilder(); + builder.ConfigureServices( + services => + { + services.AddMessageRouterServer( + s => s.UseWebSockets( + opt => + { + opt.RootPath = _webSocketUri.AbsolutePath; + opt.Port = _webSocketUri.Port; + })); + services.AddMessageRouter(mr => mr.UseServer()); + + services.AddFdc3AppDirectory( + _ => _.Source = new Uri($"file:\\\\{Directory.GetCurrentDirectory()}\\TestUtils\\appDirectorySample.json")); + + services.AddModuleLoader(); + + services.AddFdc3DesktopAgent( + fdc3 => fdc3.Configure( + builder => + { + builder.ChannelId = TestChannel; + })); + }); + + _host = builder.Build(); + await _host.StartAsync(); + + // Create a client acting in place of an application + _clientServices = new ServiceCollection() + .AddMessageRouter( + mr => mr.UseWebSocket( + new MessageRouterWebSocketOptions + { + Uri = _webSocketUri + })) + .BuildServiceProvider(); + + _messageRouter = _clientServices.GetRequiredService(); + + _moduleLoader = _host.Services.GetRequiredService(); + + _runningAppsObserver = _moduleLoader.LifetimeEvents.Subscribe((lifetimeEvent) => + { + lock (_runningAppsLock) + { + switch (lifetimeEvent.EventType) + { + case LifetimeEventType.Started: + _runningApps.Add(lifetimeEvent.Instance); + break; + + case LifetimeEventType.Stopped: + _runningApps.Remove(lifetimeEvent.Instance); + break; + } + } + }); + + var fdc3DesktopAgentMessageRouterService = _host.Services.GetRequiredService() as Fdc3DesktopAgentMessageRouterService; + _options = fdc3DesktopAgentMessageRouterService!.JsonMessageSerializerOptions; + } + + public async Task DisposeAsync() + { + List runningApps; + _runningAppsObserver?.Dispose(); + lock (_runningAppsLock) + { + runningApps = _runningApps.Reverse().ToList(); + } + foreach (var instance in runningApps) + { + await _moduleLoader.StopModule(new StopRequest(instance.InstanceId)); + } + await _clientServices.DisposeAsync(); + await _host.StopAsync(); + _host.Dispose(); + } + + [Fact] + public async void GetCurrentContextReturnsNullBeforeBroadcast() + { + var resultBuffer = await _messageRouter.InvokeAsync(_topics.GetCurrentContext, EmptyContextType); + resultBuffer.Should().BeNull(); + } + + [Fact] + public async void GetCurrentContextReturnsAfterBroadcast() + { + var ctx = GetContext(); + + await _messageRouter.PublishAsync(_topics.Broadcast, ctx); + + await Task.Delay(100); + + var resultBuffer = await _messageRouter.InvokeAsync(_topics.GetCurrentContext, ContextType); + + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(); + + result.Should().BeEquivalentTo(ctx.ReadJson()); + } + + [Fact] + public async void GetCurrentContextReturnsAfterBroadcastWithNoType() + { + var ctx = GetContext(); + + await _messageRouter.PublishAsync(_topics.Broadcast, ctx); + + await Task.Delay(100); + + var resultBuffer = await _messageRouter.InvokeAsync(_topics.GetCurrentContext, EmptyContextType); + + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(); + + result.Should().BeEquivalentTo(ctx.ReadJson()); + } + + [Fact] + public async void DifferentGetCurrentContextReturnsNullAfterBroadcast() + { + var ctx = GetContext(); + await _messageRouter.PublishAsync(_topics.Broadcast, ctx); + await Task.Delay(100); + var resultBuffer = await _messageRouter.InvokeAsync(_topics.GetCurrentContext, OtherContextType); + resultBuffer.Should().BeNull(); + } + + [Fact] + public async void FindUserChannelReturnsFoundTrueForExistingChannel() + { + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindChannel, FindRequest); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().BeEquivalentTo(FindChannelResponse.Success); + } + + [Fact] + public async void FindUserChannelReturnsNoChannelFoundForNonExistingChannel() + { + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindChannel, FindNonExistingRequest); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().BeEquivalentTo(FindChannelResponse.Failure(ChannelError.NoChannelFound)); + } + + [Fact] + public async Task FindIntentReturnsAppIntent() + { + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var appId4IntentMetadata = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }; + + var request = new FindIntentRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadata4" + }; + + var expectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = appId4IntentMetadata, + Apps = new AppMetadata[] + { + new() { AppId = "appId4", Name = "app4", ResultType = null }, + new() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new() { AppId = "appId6", Name = "app6", ResultType = "resultType" } + } + } + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentReturnsIntentDeliveryFailureBecauseOfTheRequest() + { + var expectedResponse = new FindIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntent, null); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentReturnsNoAppsFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new FindIntentRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Intent = "noAppShouldReturnIntent", + }; + + var expectedResponse = new FindIntentResponse() + { + Error = ResolveError.NoAppsFound + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentReturnsIntentDeliveryFailureBecauseOfMultipleAppIntentFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new FindIntentRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadata8" + }; + + var expectedResponse = new FindIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentsByContextReturnsAppIntent() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Context = new Context("context2"), + ResultType = "resultType" + }; + + var appId5IntentMetadata = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }; + + var expectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new AppIntent[] + { + new() + { + Intent = appId5IntentMetadata, + Apps = new AppMetadata[] + { + new() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new() { AppId = "appId6", Name = "app6", ResultType = "resultType" } + } + } + } + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntentsByContext, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentsByContextReturnsMultipleAppIntent() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Context = new Context("context9"), + ResultType = "resultWrongApp" + }; + + var appId9IntentMetadata = new Protocol.IntentMetadata() { Name = "intentMetadata9", DisplayName = "displayName9" }; + var appId12IntentMetadata = new Protocol.IntentMetadata() { Name = "intentMetadata11", DisplayName = "displayName11" }; + var expectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = appId9IntentMetadata, + Apps = new [] + { + new AppMetadata() { AppId = "wrongappId9", Name = "app9", ResultType = "resultWrongApp" }, + } + }, + new AppIntent() + { + Intent = appId12IntentMetadata, + Apps = new [] + { + new AppMetadata(){ AppId = "appId12", Name = "app12", ResultType = "resultWrongApp" }, + } + } + } + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntentsByContext, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentsByContextReturnsIntentDeliveryFailureBecauseOfTheRequest() + { + var expectedResponse = new FindIntentsByContextResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntentsByContext, null); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task FindIntentsByContextReturnsNoAppsFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = originFdc3InstanceId, + Context = new Context("context2"), + ResultType = "noAppShouldReturn" + }; + + var expectedResponse = new FindIntentsByContextResponse() + { + Error = ResolveError.NoAppsFound + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.FindIntentsByContext, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsIntentDeliveryFailureBecauseOfTheRequest() + { + var expectedResponse = new RaiseIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, null); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsErrorAsMessageContainsError() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "dummy", + Selected = false, + Context = new Context(ContextTypes.Nothing), + Error = "dummyError" + }; + + var expectedResponse = new RaiseIntentResponse() + { + Error = "dummyError" + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsNoAppsFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "noIntentShouldHandle", + Selected = false, + Context = new Context(ContextTypes.Nothing) + }; + + var expectedResponse = new RaiseIntentResponse() + { + Error = ResolveError.NoAppsFound + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsIntentDeliveryFailureAsMultipleAppIntentFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadata8", //wrongly set up AppDirectory on purpose + Selected = false, + Context = new Context(ContextTypes.Nothing) + }; + + var expectedResponse = new RaiseIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsAppIntentWithOneApp() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom") + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + var app4 = _runningApps.First(application => application.Manifest.Id == "appId4"); + var app4Fdc3InstanceId = Fdc3InstanceIdRetriever.Get(app4); + app4Fdc3InstanceId.Should().Be(resultBuffer!.ReadJson(_options)!.AppMetadata!.First().InstanceId); + app4Fdc3InstanceId.Should().NotBeNull(); + + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + + var expectedResponse = new RaiseIntentResponse() + { + MessageId = result!.MessageId, + Intent = "intentMetadataCustom", + AppMetadata = new AppMetadata[] + { + new(){ AppId = "appId4", InstanceId = app4Fdc3InstanceId, Name = "app4", ResultType = null } + } + }; + + result.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task RaiseIntentReturnsAppIntentWithOneExistingAppAndPublishesContextToHandle() + { + var origin = await _moduleLoader.StartModule(new StartRequest("appId1")); + var target = await _moduleLoader.StartModule(new StartRequest("appId4")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = + MessageBuffer.Factory.CreateJson( + new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }, + _options); + + var addIntentListenerResult = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, addIntentListenerRequest); + addIntentListenerResult.Should().NotBeNull(); + addIntentListenerResult!.ReadJson(_options).Should().BeEquivalentTo(IntentListenerResponse.SubscribeSuccess()); + + var request = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + + var expectedResponse = new RaiseIntentResponse() + { + MessageId = result!.MessageId, + Intent = "intentMetadataCustom", + AppMetadata = new AppMetadata[] + { + new() { AppId = "appId4", InstanceId = targetFdc3InstanceId, Name = "app4", ResultType = null } + } + }; + + result.Should().BeEquivalentTo(expectedResponse); + } + + //TODO: Right now we are returning just one element, without the possibility of selecting via ResolverUI. + [Fact] + public async Task RaiseIntentReturnsAppIntentWithFirstApp() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2") + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result.Should().NotBeNull(); + + var app4 = _runningApps.First(application => application.Manifest.Id == "appId4"); + var app4Fdc3InstanceId = Fdc3InstanceIdRetriever.Get(app4); + + result!.AppMetadata.Should().BeEquivalentTo( + new AppMetadata[] + { + new() { AppId = "appId4", InstanceId = app4Fdc3InstanceId, Name = "app4", ResultType = null } + }); + } + + [Fact] + public async Task StoreIntentResultReturnsIntentDeliveryFailureAsRequestIsNull() + { + var expectedResponse = new StoreIntentResultResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.SendIntentResult, null); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task StoreIntentResultReturnsIntentDeliveryFailureAsRequestNotContainsInformation() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new StoreIntentResultRequest() + { + MessageId = string.Empty, + Intent = "dummyIntent", + OriginFdc3InstanceId = originFdc3InstanceId, + TargetFdc3InstanceId = null + }; + + var expectedResponse = new StoreIntentResultResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.SendIntentResult, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task StoreIntentResultReturnsSuccessfully() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom") + }; + + var raiseIntentResultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(raiseIntentRequest, _options)); + raiseIntentResultBuffer.Should().NotBeNull(); + var raiseIntentResult = raiseIntentResultBuffer!.ReadJson(_options); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + raiseIntentResult.AppMetadata!.First().InstanceId.Should().NotBeNull(); + + var testContext = new Context("testContextType"); + + var request = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadataCustom", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + Context = testContext + }; + + var expectedResponse = new StoreIntentResultResponse() + { + Stored = true + }; + + var app4 = _runningApps.First(application => application.Manifest.Id == "appId4"); + var app4Fdc3InstanceId = Fdc3InstanceIdRetriever.Get(app4); + app4Fdc3InstanceId.Should().Be(raiseIntentResult!.AppMetadata!.First().InstanceId); + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.SendIntentResult, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task GetIntentResultReturnsIntentDeliveryFailureAsRequestIsNull() + { + var expectedResponse = new GetIntentResultResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.GetIntentResult, null); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task GetIntentResultReturnsIntentDeliveryFailureAsRequestDoesNotContainInformation() + { + var request = new GetIntentResultRequest() + { + MessageId = "dummy", + Intent = "dummy", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1" } + }; + + var expectedResponse = new GetIntentResultResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.GetIntentResult, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task GetIntentResultReturnsIntentDeliveryFailureAsNoIntentResultFound() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new GetIntentResultRequest() + { + MessageId = "dummy", + Intent = "testIntent", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1", InstanceId = originFdc3InstanceId } + }; + + var expectedResponse = new GetIntentResultResponse() + { + Error = ResolveError.IntentDeliveryFailed + }; + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.GetIntentResult, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task GetIntentResultReturnsSuccessfully() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 2, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom") + }; + + var resultRaiseIntentBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, MessageBuffer.Factory.CreateJson(raiseIntentRequest, _options)); + var raiseIntentResult = resultRaiseIntentBuffer!.ReadJson(_options); + + var testContext = new Context("testContextType"); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult!.MessageId!, + Intent = "intentMetadataCustom", + OriginFdc3InstanceId = raiseIntentResult!.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + Context = testContext + }; + + await _messageRouter.InvokeAsync(Fdc3Topic.SendIntentResult, MessageBuffer.Factory.CreateJson(storeIntentRequest, _options)); + + var request = new GetIntentResultRequest() + { + MessageId = raiseIntentResult!.MessageId!, + Intent = "intentMetadataCustom", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = raiseIntentResult!.AppMetadata!.First().InstanceId! } + }; + + var expectedResponse = new GetIntentResultResponse() + { + Context = testContext + }; + + var app4 = _runningApps.First(application => application.Manifest.Id == "appId4"); + var app4Fdc3InstanceId = Fdc3InstanceIdRetriever.Get(app4); + app4Fdc3InstanceId.Should().Be(raiseIntentResult!.AppMetadata!.First().InstanceId); + + var resultBuffer = await _messageRouter.InvokeAsync(Fdc3Topic.GetIntentResult, MessageBuffer.Factory.CreateJson(request, _options)); + resultBuffer.Should().NotBeNull(); + var result = resultBuffer!.ReadJson(_options); + result!.Should().BeEquivalentTo(expectedResponse); + } + + [Fact] + public async Task AddIntentListenerReturnsPayloadNullError() + { + var result = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, null, new()); + result.Should().NotBeNull(); + result!.ReadJson(_options).Should().BeEquivalentTo(IntentListenerResponse.Failure(Fdc3DesktopAgentErrors.PayloadNull)); + } + + [Fact] + public async Task AddIntentListenerReturnsMissingIdError() + { + var instance = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + var request = new IntentListenerRequest() { Intent = "dummy", Fdc3InstanceId = originFdc3InstanceId, State = SubscribeState.Unsubscribe }; + var expectedResponse = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, MessageBuffer.Factory.CreateJson(request, _options)); + expectedResponse.Should().NotBeNull(); + expectedResponse!.ReadJson(_options).Should().BeEquivalentTo(IntentListenerResponse.Failure(Fdc3DesktopAgentErrors.MissingId)); + } + + [Fact] + public async Task AddIntentListenerSubscribesWithExistingAppPerRaisedIntent() + { + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var origin = await _moduleLoader.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _moduleLoader.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest = MessageBuffer.Factory.CreateJson( + new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }, _options); + + var raiseIntentResult = await _messageRouter.InvokeAsync(Fdc3Topic.RaiseIntent, raiseIntentRequest); + + raiseIntentResult.Should().NotBeNull(); + var raiseIntentResponse = raiseIntentResult!.ReadJson(_options)!; + raiseIntentResponse.AppMetadata.Should().HaveCount(1); + raiseIntentResponse.AppMetadata!.First()!.AppId.Should().Be("appId4"); + + var addIntentListenerRequest = MessageBuffer.Factory.CreateJson( + new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }, _options); + + var addIntentListenerResult = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, addIntentListenerRequest); + addIntentListenerResult.Should().NotBeNull(); + + var addIntentListenerResponse = addIntentListenerResult!.ReadJson(_options); + addIntentListenerResponse!.Stored.Should().BeTrue(); + + var app4 = _runningApps.First(application => application.Manifest.Id == "appId4"); + var app4Fdc3InstanceId = Fdc3InstanceIdRetriever.Get(app4); + app4Fdc3InstanceId.Should().Be(raiseIntentResult!.ReadJson(_options)!.AppMetadata!.First().InstanceId); + } + + [Fact] + public async Task AddIntentListenerSubscribesWithNewApp() + { + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _moduleLoader.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = MessageBuffer.Factory.CreateJson( + new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }, _options); + + var addIntentListenerResult = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, addIntentListenerRequest); + addIntentListenerResult.Should().NotBeNull(); + + var addIntentListenerResponse = addIntentListenerResult!.ReadJson(_options); + addIntentListenerResponse!.Stored.Should().BeTrue(); + } + + [Fact] + public async Task AddIntentListenerUnsubscribes() + { + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _moduleLoader.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = MessageBuffer.Factory.CreateJson( + new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }, _options); + + var addIntentListenerResult = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, addIntentListenerRequest); + addIntentListenerResult.Should().NotBeNull(); + + var addIntentListnerResponse = addIntentListenerResult!.ReadJson(_options); + addIntentListnerResponse!.Stored.Should().BeTrue(); + + addIntentListenerRequest = MessageBuffer.Factory.CreateJson( + new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Unsubscribe + }, _options); + + addIntentListenerResult = await _messageRouter.InvokeAsync(Fdc3Topic.AddIntentListener, addIntentListenerRequest); + addIntentListenerResult.Should().NotBeNull(); + + addIntentListnerResponse = addIntentListenerResult!.ReadJson(_options); + addIntentListnerResponse!.Stored.Should().BeFalse(); + addIntentListnerResponse!.Error.Should().BeNull(); + } + + private int _counter = 0; + + private MessageBuffer EmptyContextType => MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest()); + + private MessageBuffer ContextType => + MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest { ContextType = new Contact().Type }); + + private MessageBuffer OtherContextType => + MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest { ContextType = new Email(null).Type }); + + private MessageBuffer GetContext() => MessageBuffer.Factory.CreateJson( + new Contact( + new ContactID() { Email = $"test{_counter}@test.org", FdsId = $"test{_counter++}" }, + "Testy Tester")); + + private MessageBuffer FindRequest => MessageBuffer.Factory.CreateJson( + new FindChannelRequest { ChannelId = TestChannel, ChannelType = ChannelType.User }); + + private MessageBuffer FindNonExistingRequest => MessageBuffer.Factory.CreateJson( + new FindChannelRequest { ChannelId = "nonexisting", ChannelType = ChannelType.User }); + } +} \ No newline at end of file diff --git a/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs b/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs new file mode 100644 index 000000000..df492b320 --- /dev/null +++ b/src/fdc3/dotnet/DesktopAgent/tests/DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs @@ -0,0 +1,1430 @@ +/* + * Morgan Stanley makes this available to you under the Apache License, + * Version 2.0 (the "License"). You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Unless required by applicable law or agreed + * to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +using Microsoft.Extensions.Logging.Abstractions; +using MorganStanley.ComposeUI.Fdc3.AppDirectory; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Contracts; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Exceptions; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests.Helpers; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests.TestUtils; +using MorganStanley.ComposeUI.ModuleLoader; +using Finos.Fdc3; +using Finos.Fdc3.AppDirectory; +using Finos.Fdc3.Context; +using AppMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppMetadata; +using AppIntent = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIntent; +using AppIdentifier = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIdentifier; +using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal; + +namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests.Infrastructure.Internal; + +public class Fdc3DesktopAgentMessageRouterServiceTests +{ + private readonly Mock _mockMessageRouter = new(); + private readonly MockModuleLoader _mockModuleLoader = new(); + private readonly IAppDirectory _appDirectory = new AppDirectory.AppDirectory( + new AppDirectoryOptions() + { + Source = new Uri($"file:\\\\{Directory.GetCurrentDirectory()}\\TestUtils\\appDirectorySample.json") + }); + + private readonly Fdc3DesktopAgentMessageRouterService _fdc3; + private const string TestChannel = "testChannel"; + + public Fdc3DesktopAgentMessageRouterServiceTests() + { + _fdc3 = new( + _mockMessageRouter.Object, + new Fdc3DesktopAgent(_appDirectory, _mockModuleLoader.Object, new Fdc3DesktopAgentOptions(), NullLoggerFactory.Instance), + new Fdc3DesktopAgentOptions(), + NullLoggerFactory.Instance); + } + + [Fact] + public async void UserChannelAddedCanBeFound() + { + await _fdc3.HandleAddUserChannel(TestChannel); + + var result = await _fdc3.HandleFindChannel(FindTestChannel, new MessageContext()); + + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(FindChannelResponse.Success); + } + + [Fact] + public async Task RaiseIntent_returns_one_app_by_AppIdentifier() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4" } + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + result!.AppMetadata!.First()!.InstanceId.Should().NotBeNull(); + } + + [Fact] + public async Task RaiseIntent_fails_by_request_delivery_error() + { + var result = await _fdc3.HandleRaiseIntent(null, new MessageContext()); + + result.Should().NotBeNull(); + result!.Error.Should().Be(ResolveError.IntentDeliveryFailed); + } + + [Fact] + public async Task RaiseIntent_returns_one_app_by_Context() + { + var request = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom") + }; + + var result = await _fdc3.HandleRaiseIntent(request, new MessageContext()); + + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + result!.AppMetadata!.First()!.InstanceId.Should().NotBeNull(); + } + + [Fact] + public async Task RaiseIntent_returns_one_app_by_AppIdentifier_and_saves_context_to_resolve_it_when_registers_its_intentHandler() + { + await _fdc3.StartAsync(CancellationToken.None); + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var instance = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(instance); + + var request = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var result = await _fdc3.HandleRaiseIntent(request, new MessageContext()); + + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + result!.AppMetadata!.First()!.InstanceId.Should().Be(targetFdc3InstanceId); + + _mockMessageRouter.Verify( + _ => _.InvokeAsync(Fdc3Topic.AddIntentListener, It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + + _mockMessageRouter.Verify( + _ => _.InvokeAsync(Fdc3Topic.RaiseIntentResolution("intentMetadataCustom", targetFdc3InstanceId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task RaiseIntent_returns_one_app_by_AppIdentifier_and_publishes_context_to_resolve_it_when_registers_its_intentHandler() + { + await _fdc3.StartAsync(CancellationToken.None); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var origin = await _mockModuleLoader.Object.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }; + + var addIntentListenerResult = await _fdc3.HandleAddIntentListener(addIntentListenerRequest, new MessageContext()); + addIntentListenerResult.Should().NotBeNull(); + + addIntentListenerResult!.Stored.Should().BeTrue(); + + var request = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var result = await _fdc3.HandleRaiseIntent(request, new MessageContext()); + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + result!.AppMetadata!.First()!.InstanceId.Should().Be(targetFdc3InstanceId); + + _mockMessageRouter.Verify( + _ => _.PublishAsync(Fdc3Topic.RaiseIntentResolution("intentMetadataCustom", targetFdc3InstanceId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + //TODO: Right now we are returning just one element, without the possibility of selecting via ResolverUI. + [Fact] + public async Task RaiseIntent_returns_first_app_by_Context() + { + var instanceId = Guid.NewGuid().ToString(); + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = instanceId, + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2") + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First().AppId.Should().Be("appId4"); + } + + //TODO: Right now we are returning just one element, without the possibility of selecting via ResolverUI. + [Fact] + public async Task RaiseIntent_returns_first_app_by_Context_if_fdc3_nothing() + { + var instanceId = Guid.NewGuid().ToString(); + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = instanceId, + Intent = "intentMetadata4", + Selected = false, + Context = new Context(ContextTypes.Nothing) + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + + result!.AppMetadata.Should().HaveCount(1); + result!.AppMetadata!.First().AppId.Should().Be("appId4"); + } + + [Fact] + public async Task RaiseIntent_fails_as_no_apps_found_by_AppIdentifier() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "testIntent", + Selected = false, + Context = new Context("contextType"), + TargetAppIdentifier = new AppIdentifier() { AppId = "noAppShouldReturn" } + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Error.Should().Be(ResolveError.NoAppsFound); + } + + [Fact] + public async Task RaiseIntent_fails_as_no_apps_found_by_Context() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("noAppShouldReturn") + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + + result.Should().NotBeNull(); + result!.Error.Should().Be(ResolveError.NoAppsFound); + } + + [Fact] + public async Task RaiseIntent_fails_as_no_apps_found_by_Intent() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "noAppShouldReturn", + Selected = false, + Context = new Context("context2") + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + + result.Should().NotBeNull(); + result!.Error.Should().Be(ResolveError.NoAppsFound); + } + + [Fact] + public async Task RaiseIntent_fails_as_multiple_IAppIntents_found() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata8", + Selected = false, + Context = new Context("context7") + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + + result.Should().NotBeNull(); + result!.Error.Should().Be(ResolveError.IntentDeliveryFailed); + } + + [Fact] + public async Task RaiseIntent_fails_as_request_specifies_error() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "someIntent", + Selected = false, + Error = "Some weird error" + }; + + var result = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + + result!.Error.Should().Be("Some weird error"); + } + + [Fact] + public async Task StoreIntentResult_fails_due_the_request() + { + var result = await _fdc3.HandleStoreIntentResult(null, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new StoreIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed, Stored = false }); + } + + [Fact] + public async Task StoreIntentResult_fails_due_the_request_contains_no_information() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4" } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "dummy", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = null, + ChannelId = "dummyChannelId", + ChannelType = ChannelType.User, + }; + + var result = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new StoreIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed, Stored = false }); + } + + [Fact] + public async Task StoreIntentResult_fails_due_the_previosly_no_saved_raiseIntent_could_handle() + { + var originFdc3InstanceId = Guid.NewGuid().ToString(); + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = "dummy", + Intent = "dummy", + OriginFdc3InstanceId = originFdc3InstanceId, + TargetFdc3InstanceId = Guid.NewGuid().ToString(), + ChannelId = "dummyChannelId", + ChannelType = ChannelType.User + }; + + var action = async () => await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + + await action.Should() + .ThrowAsync(); + } + + [Fact] + public async Task StoreIntentResult_succeeds_with_channel() + { + await _fdc3.StartAsync(CancellationToken.None); + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.Error.Should().BeNull(); + raiseIntentResult.AppMetadata.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult!.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = Guid.NewGuid().ToString(), + ChannelId = "dummyChannelId", + ChannelType = ChannelType.User + }; + + var result = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new StoreIntentResultResponse() { Stored = true }); + } + + [Fact] + public async Task StoreIntentResult_succeeds_with_context() + { + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = true, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4" } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = Guid.NewGuid().ToString(), + ChannelId = null, + ChannelType = null, + Context = new Context("test") + }; + + var result = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new StoreIntentResultResponse() { Stored = true }); + } + + [Fact] + public async Task StoreIntentResult_succeeds_with_voidResult() + { + await _fdc3.StartAsync(CancellationToken.None); + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = Guid.NewGuid().ToString(), + ChannelId = null, + ChannelType = null, + Context = null, + VoidResult = true + }; + + var result = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new StoreIntentResultResponse() { Stored = true }); + } + + [Fact] + public async Task GetIntentResult_fails_due_the_request() + { + var result = await _fdc3.HandleGetIntentResult(null, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new GetIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed }); + } + + [Fact] + public async Task GetIntentResult_fails_intent_not_found() + { + //Version should be the Intent's schema version + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = "dummy", + Intent = "dummy", + TargetAppIdentifier = new AppIdentifier() { AppId = "dummy", InstanceId = Guid.NewGuid().ToString() }, + Version = "1.0" + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new GetIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed }); + } + + [Fact] + public async Task GetIntentResult_fails_due_InstanceId_is_null() + { + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = "dummy", + Intent = "dummy", + TargetAppIdentifier = new AppIdentifier() { AppId = "dummy" }, + Version = "1.0" + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new GetIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed }); + } + + [Fact] + public async Task GetIntentResult_fails_due_no_intent_found() + { + await _fdc3.StartAsync(CancellationToken.None); + var originFdc3InstanceId = Guid.NewGuid().ToString(); + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var context = new Context("test"); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + Context = context + }; + + var storeResult = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + storeResult.Should().NotBeNull(); + storeResult!.Should().BeEquivalentTo(StoreIntentResultResponse.Success()); + + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "dummy", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1", InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId! }, + Version = "1.0" + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(new GetIntentResultResponse() { Error = ResolveError.IntentDeliveryFailed }); + } + + [Fact] + public async Task GetIntentResult_succeeds_with_context() + { + await _fdc3.StartAsync(CancellationToken.None); + var originFdc3InstanceId = Guid.NewGuid().ToString(); + var context = new Context("test"); + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + Context = context + }; + + var storeResult = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + + storeResult.Should().NotBeNull(); + storeResult!.Should().BeEquivalentTo(StoreIntentResultResponse.Success()); + + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1", InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId! } + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(GetIntentResultResponse.Success(context: context)); + } + + [Fact] + public async Task GetIntentResult_succeeds_with_channel() + { + await _fdc3.StartAsync(CancellationToken.None); + var originFdc3InstanceId = Guid.NewGuid().ToString(); + var channelType = ChannelType.User; + var channelId = "dummyChannelId"; + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest =new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + ChannelType = channelType, + ChannelId = channelId + }; + + var storeResult = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + + storeResult.Should().NotBeNull(); + storeResult!.Should().BeEquivalentTo(StoreIntentResultResponse.Success()); + + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1", InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId! } + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(GetIntentResultResponse.Success(channelType: channelType, channelId: channelId)); + } + + [Fact] + public async Task GetIntentResult_succeeds_with_voidResult() + { + await _fdc3.StartAsync(CancellationToken.None); + var originFdc3InstanceId = Guid.NewGuid().ToString(); + + var target = await _mockModuleLoader.Object.StartModule(new("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = int.MaxValue, + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Selected = false, + Context = new Context("context2"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + + var storeIntentRequest = new StoreIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + OriginFdc3InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId!, + TargetFdc3InstanceId = originFdc3InstanceId, + VoidResult = true + }; + + var storeResult = await _fdc3.HandleStoreIntentResult(storeIntentRequest, new MessageContext()); + storeResult.Should().NotBeNull(); + storeResult!.Should().BeEquivalentTo(StoreIntentResultResponse.Success()); + + var getIntentResultRequest = new GetIntentResultRequest() + { + MessageId = raiseIntentResult.MessageId!, + Intent = "intentMetadata4", + TargetAppIdentifier = new AppIdentifier() { AppId = "appId1", InstanceId = raiseIntentResult.AppMetadata!.First().InstanceId! } + }; + + var result = await _fdc3.HandleGetIntentResult(getIntentResultRequest, new MessageContext()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(GetIntentResultResponse.Success(voidResult: true)); + } + + [Fact] + public async Task AddIntentListener_fails_due_no_payload() + { + var result = await _fdc3.HandleAddIntentListener(null, new()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(IntentListenerResponse.Failure(Fdc3DesktopAgentErrors.PayloadNull)); + } + + [Fact] + public async Task AddIntentListener_fails_due_missing_id() + { + var request = new IntentListenerRequest() + { + Intent = "dummy", + Fdc3InstanceId = Guid.NewGuid().ToString(), + State = SubscribeState.Unsubscribe + }; + var result = await _fdc3.HandleAddIntentListener(request, new()); + result.Should().NotBeNull(); + result!.Should().BeEquivalentTo(IntentListenerResponse.Failure(Fdc3DesktopAgentErrors.MissingId)); + } + + [Fact] + public async Task AddIntentListener_subscribes_to_existing_raised_intent() + { + await _fdc3.StartAsync(CancellationToken.None); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var origin = await _mockModuleLoader.Object.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + raiseIntentResult!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + raiseIntentResult!.AppMetadata!.First()!.InstanceId.Should().Be(targetFdc3InstanceId); + + var addIntentListenerRequest = new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }; + + var addIntentListenerResult = await _fdc3.HandleAddIntentListener(addIntentListenerRequest, new MessageContext()); + addIntentListenerResult.Should().NotBeNull(); + addIntentListenerResult!.Stored.Should().BeTrue(); + + _mockMessageRouter.Verify( + _ => _.PublishAsync(Fdc3Topic.RaiseIntentResolution("intentMetadataCustom", targetFdc3InstanceId), It.IsAny(), It.IsAny(), It.IsAny())); + } + + [Fact] + public async Task AddIntentListener_subscribes() + { + await _fdc3.StartAsync(CancellationToken.None); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var origin = await _mockModuleLoader.Object.StartModule(new StartRequest("appId1")); + var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }; + + var addIntentListenerResult = await _fdc3.HandleAddIntentListener(addIntentListenerRequest, new MessageContext()); + addIntentListenerResult.Should().NotBeNull(); + addIntentListenerResult!.Stored.Should().BeTrue(); + + var raiseIntentRequest = new RaiseIntentRequest() + { + MessageId = 1, + Fdc3InstanceId = originFdc3InstanceId, + Intent = "intentMetadataCustom", + Selected = false, + Context = new Context("contextCustom"), + TargetAppIdentifier = new AppIdentifier() { AppId = "appId4", InstanceId = targetFdc3InstanceId } + }; + + var raiseIntentResult = await _fdc3.HandleRaiseIntent(raiseIntentRequest, new MessageContext()); + raiseIntentResult.Should().NotBeNull(); + raiseIntentResult!.AppMetadata.Should().HaveCount(1); + raiseIntentResult!.AppMetadata!.First()!.AppId.Should().Be("appId4"); + raiseIntentResult!.AppMetadata!.First()!.InstanceId.Should().Be(targetFdc3InstanceId); + + _mockMessageRouter.Verify( + _ => _.PublishAsync(Fdc3Topic.RaiseIntentResolution("intentMetadataCustom", targetFdc3InstanceId), It.IsAny(), It.IsAny(), It.IsAny())); + } + + [Fact] + public async Task AddIntentListener_unsubscribes() + { + await _fdc3.StartAsync(CancellationToken.None); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var origin = await _mockModuleLoader.Object.StartModule(new StartRequest("appId1")); + + //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id + var target = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4")); + var targetFdc3InstanceId = Fdc3InstanceIdRetriever.Get(target); + + var addIntentListenerRequest = new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Subscribe + }; + + var addIntentListenerResult = await _fdc3.HandleAddIntentListener(addIntentListenerRequest, new MessageContext()); + addIntentListenerResult.Should().NotBeNull(); + addIntentListenerResult!.Stored.Should().BeTrue(); + + addIntentListenerRequest = new IntentListenerRequest() + { + Intent = "intentMetadataCustom", + Fdc3InstanceId = targetFdc3InstanceId, + State = SubscribeState.Unsubscribe + }; + + addIntentListenerResult = await _fdc3.HandleAddIntentListener(addIntentListenerRequest, new MessageContext()); + addIntentListenerResult.Should().NotBeNull(); + addIntentListenerResult!.Stored.Should().BeFalse(); + addIntentListenerResult!.Error.Should().BeNull(); + } + + private FindChannelRequest FindTestChannel => new FindChannelRequest() { ChannelId = "testChannel", ChannelType = ChannelType.User }; + + + [Theory] + [ClassData(typeof(FindIntentTheoryData))] + public async Task FindIntent_edge_case_tests(FindIntentTestCase testCase) + { + var request = testCase.Request; + var result = await _fdc3.HandleFindIntent(request, new MessageContext()); + + result.Should().NotBeNull(); + + if (testCase.ExpectedAppCount > 0) + { + result!.AppIntent!.Apps.Should().HaveCount(testCase.ExpectedAppCount); + } + + result!.Should().BeEquivalentTo(testCase.ExpectedResponse); + } + + [Theory] + [ClassData(typeof(FindIntentsByContextTheoryData))] + public async Task FindIntentsByContext_edge_case_tests(FindIntentsByContextTestCase testCase) + { + var request = testCase.Request; + var result = await _fdc3.HandleFindIntentsByContext(request, new MessageContext()); + + result.Should().NotBeNull(); + + if (testCase.ExpectedAppIntentsCount > 0) + { + result!.AppIntents!.Should().HaveCount(testCase.ExpectedAppIntentsCount); + } + + result!.Should().BeEquivalentTo(testCase.ExpectedResponse); + } + + public class FindIntentsByContextTheoryData : TheoryData + { + public FindIntentsByContextTheoryData() + { + // Returning one AppIntent with one app by just passing Context + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("contextCustom") + },//This relates to the appId4 only + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadataCustom", DisplayName = "intentMetadataCustom" }, + Apps = new [] + { + new AppMetadata(){ AppId ="appId4", Name = "app4", ResultType = null } + } + } + } + }, + ExpectedAppIntentsCount = 1 + }); + + // Returning one AppIntent with multiple app by just passing Context + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context2") + }, //This relates to the appId4, appId5, appId6, + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new AppMetadata[] + { + new() { AppId = "appId4", Name = "app4", ResultType = null }, + new() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new() { AppId = "appId6", Name = "app6", ResultType = "resultType" } + } + } + } + }, + ExpectedAppIntentsCount = 1 + }); + + // Returning multiple appIntent by just passing Context + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context9") + },//This relates to the wrongappId9 and an another wrongAppId9 with 2 individual IntentMetadata + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata9", DisplayName = "displayName9" }, + Apps = new [] + { + new AppMetadata() + { + AppId = "wrongappId9", Name = "app9", ResultType = "resultWrongApp" + }, + } + }, + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata10", DisplayName = "displayName10" }, + Apps = new [] + { + new AppMetadata(){ AppId = "appId11", Name = "app11", ResultType = "channel" }, + } + }, + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata11", DisplayName = "displayName11" }, + Apps = new [] + { + new AppMetadata() { AppId = "appId12", Name = "app12", ResultType = "resultWrongApp"}, + } + } + } + }, + ExpectedAppIntentsCount = 3 + }); + + // Returning error no apps found by just passing Context + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("noAppShouldReturn") + },// no app should have this context type + ExpectedResponse = new FindIntentsByContextResponse() + { + Error = ResolveError.NoAppsFound + }, + ExpectedAppIntentsCount = 0 + }); + + // Returning one AppIntent with one app by ResultType + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context2"), //This relates to multiple appId + ResultType = "resultType" + }, + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent(){ + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata4", DisplayName = "displayName4" }, // it should just return appId5 + Apps = new [] + { + new AppMetadata(){ AppId = "appId5", Name = "app5", ResultType = "resultType" } + } + } + } + }, + ExpectedAppIntentsCount = 1 + }); + + // Returning one AppIntent with multiple apps by ResultType + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context2"), //This relates to multiple appId + ResultType = "resultType" + }, + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new[] + { + new AppMetadata(){ AppId ="appId5", Name = "app5", ResultType = "resultType" }, + new AppMetadata(){ AppId ="appId6", Name = "app6", ResultType = "resultType" } + } + }, + } + }, + ExpectedAppIntentsCount = 1 + }); + + // Returning multiple AppIntent by ResultType + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context9"), //This relates to multiple appId + ResultType = "resultWrongApp" + }, + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata9", DisplayName = "displayName9" }, + Apps = new [] + { + new AppMetadata() { AppId = "wrongappId9", Name = "app9", ResultType = "resultWrongApp" }, + } + }, + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata11", DisplayName = "displayName11" }, + Apps = new [] + { + new AppMetadata() { AppId = "appId12", Name = "app12", ResultType = "resultWrongApp" } + } + } + } + }, + ExpectedAppIntentsCount = 2 + }); + + // Returning no apps found error by using ResultType + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context("context9"), //This relates to multiple appId + ResultType = "noAppShouldReturn" + }, + ExpectedResponse = new FindIntentsByContextResponse() + { + Error = ResolveError.NoAppsFound + }, + ExpectedAppIntentsCount = 0 + }); + + // Returning intent delivery error + AddRow(new FindIntentsByContextTestCase() + { + Request = null, + ExpectedResponse = new FindIntentsByContextResponse() + { + Error = ResolveError.IntentDeliveryFailed + }, + ExpectedAppIntentsCount = 0 + }); + + // Returning all the apps that are using the ResultType by adding fdc3.nothing. + AddRow(new FindIntentsByContextTestCase() + { + Request = new FindIntentsByContextRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Context = new Context(ContextTypes.Nothing), + ResultType = "resultWrongApp" + }, + ExpectedResponse = new FindIntentsByContextResponse() + { + AppIntents = new[] + { + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata9", DisplayName = "displayName9" }, + Apps = new [] + { + new AppMetadata() { AppId = "wrongappId9", Name = "app9", ResultType = "resultWrongApp" }, + } + }, + + new AppIntent() + { + Intent = new Protocol.IntentMetadata () { Name = "intentMetadata11", DisplayName = "displayName11" }, + Apps = new [] + { + new AppMetadata() { AppId = "appId12", Name = "app12", ResultType = "resultWrongApp" } + } + }, + } + }, + ExpectedAppIntentsCount = 2 + }); + } + } + + public class FindIntentsByContextTestCase + { + internal FindIntentsByContextRequest Request { get; set; } + internal FindIntentsByContextResponse ExpectedResponse { get; set; } + public int ExpectedAppIntentsCount { get; set; } + } + + private class FindIntentTheoryData : TheoryData + { + public FindIntentTheoryData() + { + //As per the documentation : https://github.com/morganstanley/fdc3-dotnet/blob/main/src/Fdc3/IIntentMetadata.cs + //name is unique for the intents, so it should be unique for every app, or the app should have the same intentMetadata? + //if so we should return multiple appIntents and do not return error message for the client. + //We have setup a test case for wrongappId9 which contains wrongly setted up intentMetadata. + AddRow( + new FindIntentTestCase() + { + ExpectedAppCount = 0, + ExpectedResponse = new FindIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }, + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata8" + } + }); + + AddRow( + new FindIntentTestCase() + { + ExpectedAppCount = 0, + ExpectedResponse = new FindIntentResponse() + { + Error = ResolveError.NoAppsFound + }, + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata2", + Context = new Context("noAppShouldBeReturned") + } + }); + + AddRow( + new FindIntentTestCase() + { + ExpectedAppCount = 0, + ExpectedResponse = new FindIntentResponse() + { + Error = ResolveError.IntentDeliveryFailed + }, + Request = null + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata7", + Context = new Context("context8"), + ResultType = "resultType2" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadat7", DisplayName = "displayName7" }, + Apps = new[] + { + new AppMetadata(){ AppId = "appId7", Name = "app7", ResultType = "resultType2" } + } + } + }, + ExpectedAppCount = 1 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Context = new Context("context2"), + ResultType = "resultType" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new[] + { + new AppMetadata() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new AppMetadata() { AppId = "appId6", Name = "app6", ResultType = "resultType"}, + + } + } + }, + ExpectedAppCount = 2 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata7", + ResultType = "resultType2" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadat7", DisplayName = "displayName7" }, + Apps = new[] + { + new AppMetadata() { AppId = "appId7", Name = "app7", ResultType = "resultType2"} + } + } + }, + ExpectedAppCount = 1 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + ResultType = "resultType" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new[] + { + new AppMetadata() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new AppMetadata() { AppId = "appId6", Name = "app6", ResultType = "resultType" }, + } + } + }, + ExpectedAppCount = 2 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata1", + Context = new Context("context1") + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata1", DisplayName = "displayName1" }, + Apps = new[] + { + new AppMetadata() { AppId = "appId1", Name = "app1", ResultType = null } + } + } + }, + ExpectedAppCount = 1 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4", + Context = new Context("context2") + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new AppMetadata[] + { + new() { AppId = "appId4", Name = "app4", ResultType = null }, + new() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new() { AppId = "appId6", Name = "app6", ResultType = "resultType" } + } + } + }, + ExpectedAppCount = 3 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata2" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata2", DisplayName = "displayName2" }, + Apps = new[] + { + new AppMetadata() { AppId = "appId2", Name = "app2", ResultType = null } + } + } + }, + ExpectedAppCount = 1 + }); + + AddRow(new FindIntentTestCase() + { + Request = new FindIntentRequest() + { + Fdc3InstanceId = Guid.NewGuid().ToString(), + Intent = "intentMetadata4" + }, + ExpectedResponse = new FindIntentResponse() + { + AppIntent = new AppIntent() + { + Intent = new Protocol.IntentMetadata() { Name = "intentMetadata4", DisplayName = "displayName4" }, + Apps = new AppMetadata[] + { + new() { AppId = "appId4", Name = "app4", ResultType = null }, + new() { AppId = "appId5", Name = "app5", ResultType = "resultType" }, + new() { AppId = "appId6", Name = "app6", ResultType = "resultType" } + } + } + }, + ExpectedAppCount = 3 + }); + } + } + + public class FindIntentTestCase + { + internal FindIntentRequest Request { get; set; } + internal FindIntentResponse ExpectedResponse { get; set; } + public int ExpectedAppCount { get; set; } + } +}