diff --git a/Emulsion.Tests/Emulsion.Tests.fsproj b/Emulsion.Tests/Emulsion.Tests.fsproj
index 3491ce81..2300bc39 100644
--- a/Emulsion.Tests/Emulsion.Tests.fsproj
+++ b/Emulsion.Tests/Emulsion.Tests.fsproj
@@ -10,7 +10,7 @@
-
+
@@ -20,8 +20,9 @@
-
+
+
diff --git a/Emulsion.Tests/Settings.fs b/Emulsion.Tests/SettingsTests.fs
similarity index 96%
rename from Emulsion.Tests/Settings.fs
rename to Emulsion.Tests/SettingsTests.fs
index 2045bc31..596c1b80 100644
--- a/Emulsion.Tests/Settings.fs
+++ b/Emulsion.Tests/SettingsTests.fs
@@ -1,4 +1,4 @@
-module Emulsion.Tests.Settings
+module Emulsion.Tests.SettingsTests
open System
open System.IO
diff --git a/Emulsion.Tests/Xmpp/EmulsionXmppTests.fs b/Emulsion.Tests/Xmpp/EmulsionXmppTests.fs
new file mode 100644
index 00000000..ac263001
--- /dev/null
+++ b/Emulsion.Tests/Xmpp/EmulsionXmppTests.fs
@@ -0,0 +1,170 @@
+module Emulsion.Tests.Xmpp.EmulsionXmppTests
+
+open System
+open System.Threading.Tasks
+
+open JetBrains.Lifetimes
+open SharpXMPP
+open Xunit
+open Xunit.Abstractions
+
+open Emulsion.Settings
+open Emulsion
+open Emulsion.Lifetimes
+open Emulsion.Tests.TestUtils
+open Emulsion.Tests.Xmpp
+open Emulsion.Xmpp
+open Emulsion.Xmpp.SharpXmppHelper.Elements
+
+let private settings = {
+ Login = "user@example.org"
+ Password = "password"
+ Room = "room@conference.example.org"
+ Nickname = "nickname"
+}
+
+type RunTests(outputHelper: ITestOutputHelper) =
+ let logger = Logging.xunitLogger outputHelper
+
+ []
+ member __.``EmulsionXmpp connects the server``(): unit =
+ let mutable connectionFailedHandler = ignore
+ let disconnect() = connectionFailedHandler(ConnFailedArgs())
+ let mutable connectCalled = false
+ let client =
+ XmppClientFactory.create(
+ addConnectionFailedHandler = (fun _ h -> connectionFailedHandler <- h),
+ connect = (fun () -> async {
+ connectCalled <- true
+ disconnect()
+ })
+ )
+ Assert.ThrowsAny(fun() -> Async.RunSynchronously <| EmulsionXmpp.run settings logger client ignore)
+ |> ignore
+ Assert.True connectCalled
+
+ []
+ member __.``EmulsionXmpp connects the room``(): unit =
+ let mutable connectionFailedHandler = ignore
+ let disconnect() = connectionFailedHandler(ConnFailedArgs())
+ let mutable joinRoomArgs = Unchecked.defaultof<_>
+ let client =
+ XmppClientFactory.create(
+ addConnectionFailedHandler = (fun _ h -> connectionFailedHandler <- h),
+ joinMultiUserChat = (fun roomJid nickname ->
+ joinRoomArgs <- (roomJid.FullJid, nickname)
+ disconnect()
+ )
+ )
+ Assert.ThrowsAny(fun() -> Async.RunSynchronously <| EmulsionXmpp.run settings logger client ignore)
+ |> ignore
+ Assert.Equal((settings.Room, settings.Nickname), joinRoomArgs)
+
+type ReceiveMessageTests(outputHelper: ITestOutputHelper) =
+ let logger = Logging.xunitLogger outputHelper
+
+ let runReceiveMessageTest message =
+ let mutable connectionFailedHandler = ignore
+ let receiveHandlers = ResizeArray()
+
+ let sendMessage msg = receiveHandlers |> Seq.iter (fun h -> h msg)
+ let disconnect() = connectionFailedHandler(ConnFailedArgs())
+
+ let mutable messageReceived = None
+ let onMessageReceived = fun m -> messageReceived <- Some m
+
+ let client =
+ XmppClientFactory.create(
+ addConnectionFailedHandler = (fun _ h -> connectionFailedHandler <- h),
+ addMessageHandler = (fun _ h -> receiveHandlers.Add h),
+ joinMultiUserChat = fun _ _ ->
+ sendMessage message
+ disconnect()
+ )
+ Assert.ThrowsAny(fun() ->
+ Async.RunSynchronously <| EmulsionXmpp.run settings logger client onMessageReceived
+ ) |> ignore
+
+ messageReceived
+
+ []
+ member __.``Ordinary message gets received by the client``(): unit =
+ let incomingMessage = XmppMessageFactory.create("room@conference.example.org/sender",
+ "test",
+ messageType = "groupchat")
+ let receivedMessage = runReceiveMessageTest incomingMessage
+ Assert.Equal(Some <| XmppMessage { author = "sender"; text = "test" }, receivedMessage)
+
+ []
+ member __.``Own message gets skipped by the client``(): unit =
+ let ownMessage = XmppMessageFactory.create("room@conference.example.org/nickname",
+ "test",
+ messageType = "groupchat")
+ let receivedMessage = runReceiveMessageTest ownMessage
+ Assert.Equal(None, receivedMessage)
+
+ []
+ member __.``Historical message gets skipped by the client``(): unit =
+ let historicalMessage = XmppMessageFactory.create("room@conference.example.org/sender",
+ "test",
+ messageType = "groupchat",
+ delayDate = "2019-01-01")
+ let receivedMessage = runReceiveMessageTest historicalMessage
+ Assert.Equal(None, receivedMessage)
+
+ []
+ member __.``Empty message gets skipped by the client``(): unit =
+ let emptyMessage = XmppMessageFactory.create("room@conference.example.org/sender",
+ "",
+ messageType = "groupchat")
+ let receivedMessage = runReceiveMessageTest emptyMessage
+ Assert.Equal(None, receivedMessage)
+
+type SendTests(outputHelper: ITestOutputHelper) =
+ let logger = Logging.xunitLogger outputHelper
+
+ []
+ member __.``send function calls the Send method on the client``(): unit =
+ use ld = Lifetime.Define()
+ let lt = ld.Lifetime
+ let mutable sentMessage = Unchecked.defaultof<_>
+ let client = XmppClientFactory.create(send = fun m ->
+ sentMessage <- m
+ ld.Terminate()
+ )
+
+ let outgoingMessage = { author = "author"; text = "text" }
+ Assert.Throws(fun () ->
+ Async.RunSynchronously <| EmulsionXmpp.send logger client lt settings outgoingMessage
+ ) |> ignore
+
+ let text = sentMessage.Element(Body).Value
+ Assert.Equal(" text", text)
+
+ []
+ member __.``send function awaits the message delivery``(): Task =
+ upcast (async {
+ use ld = Lifetime.Define()
+ let lt = ld.Lifetime
+ let messageId = nestedTaskCompletionSource lt
+ let messageHandlers = ResizeArray()
+ let onMessage msg = messageHandlers |> Seq.iter (fun h -> h msg)
+
+ let client =
+ XmppClientFactory.create(
+ addMessageHandler = (fun _ h -> messageHandlers.Add h),
+ send = fun m -> messageId.SetResult(SharpXmppHelper.getMessageId m)
+ )
+ let outgoingMessage = { author = "author"; text = "text" }
+
+ let! receival = Async.StartChild <| EmulsionXmpp.send logger client lt settings outgoingMessage
+ let receivalTask = Async.StartAsTask receival
+ let! messageId = Async.AwaitTask messageId.Task // the send has been completed
+
+ // Wait for 100 ms to check that the receival is not completed yet:
+ Assert.False(receivalTask.Wait(TimeSpan.FromMilliseconds 100.0))
+
+ let deliveryMessage = SharpXmppHelper.message messageId "" ""
+ onMessage deliveryMessage
+ do! receival
+ } |> Async.StartAsTask)
diff --git a/Emulsion.Tests/Xmpp/SharpXmppHelper.fs b/Emulsion.Tests/Xmpp/SharpXmppHelperTests.fs
similarity index 98%
rename from Emulsion.Tests/Xmpp/SharpXmppHelper.fs
rename to Emulsion.Tests/Xmpp/SharpXmppHelperTests.fs
index a7cb773a..68e6189e 100644
--- a/Emulsion.Tests/Xmpp/SharpXmppHelper.fs
+++ b/Emulsion.Tests/Xmpp/SharpXmppHelperTests.fs
@@ -1,4 +1,4 @@
-module Emulsion.Tests.Xmpp.SharpXmppHelper
+module Emulsion.Tests.Xmpp.SharpXmppHelperTests
open System.Xml.Linq
diff --git a/Emulsion.Tests/Xmpp/XmppClientTests.fs b/Emulsion.Tests/Xmpp/XmppClientTests.fs
index eb14865d..9f267dd6 100644
--- a/Emulsion.Tests/Xmpp/XmppClientTests.fs
+++ b/Emulsion.Tests/Xmpp/XmppClientTests.fs
@@ -1,4 +1,4 @@
-namespace Emulsion.Tests.Xmpp
+module Emulsion.Tests.Xmpp.XmppClientTests
open System
open System.Threading.Tasks
@@ -9,223 +9,218 @@ open SharpXMPP
open SharpXMPP.XMPP
open SharpXMPP.XMPP.Client.Elements
open Xunit
-open Xunit.Abstractions
-open Emulsion.Tests.TestUtils
open Emulsion.Xmpp
open Emulsion.Xmpp.SharpXmppHelper.Attributes
open Emulsion.Xmpp.SharpXmppHelper.Elements
-type XmppClientTests(testOutput: ITestOutputHelper) =
- let logger = Logging.xunitLogger testOutput
-
- let createPresenceFor (roomJid: JID) nickname =
- let presence = XMPPPresence()
- let participantJid = JID(roomJid.FullJid)
- participantJid.Resource <- nickname
- presence.SetAttributeValue(From, participantJid.FullJid)
- presence
-
- let createSelfPresence roomJid nickname =
- let presence = createPresenceFor roomJid nickname
- let x = XElement X
- let status = XElement Status
- status.SetAttributeValue(Code, "110")
- x.Add status
- presence.Add x
- presence
-
- let createErrorPresence roomJid nickname errorXml =
- let presence = createPresenceFor roomJid nickname
- presence.SetAttributeValue(Type, "error")
- let error = XElement Error
- let errorChild = XElement.Parse errorXml
- error.Add errorChild
- presence.Add error
- presence
-
- let createLeavePresence roomJid nickname =
- let presence = createSelfPresence roomJid nickname
- presence.SetAttributeValue(Type, "unavailable")
- presence
-
- let sendPresence presence handlers =
- Seq.iter (fun h -> h presence) handlers
-
- let createErrorMessage (message: XMPPMessage) errorXml =
- // An error message is an exact copy of the original with the "error" element added:
- let errorMessage = XMPPMessage()
- message.Attributes() |> Seq.iter (fun a -> errorMessage.SetAttributeValue(a.Name, a.Value))
- message.Elements() |> Seq.iter (fun e -> errorMessage.Add e)
-
- let error = XElement Error
- let errorChild = XElement.Parse errorXml
- error.Add errorChild
- errorMessage.Add error
- errorMessage
-
- []
- member __.``connect function calls the Connect method of the client passed``(): unit =
- let mutable connectCalled = false
- let client = XmppClientFactory.create(fun () -> async { connectCalled <- true })
- Async.RunSynchronously <| XmppClient.connect logger client |> ignore
- Assert.True connectCalled
-
- []
- member __.``connect function returns a lifetime terminated whenever the ConnectionFailed callback is triggered``()
- : unit =
- let mutable callback = ignore
- let client = XmppClientFactory.create(addConnectionFailedHandler = fun _ h -> callback <- h)
- let lt = Async.RunSynchronously <| XmppClient.connect logger client
- Assert.True lt.IsAlive
- callback(ConnFailedArgs())
- Assert.False lt.IsAlive
-
- []
- member __.``enter function calls JoinMultiUserChat``(): unit =
- let mutable called = false
- let mutable presenceHandlers = ResizeArray()
- let client =
- XmppClientFactory.create(
- addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
- joinMultiUserChat = fun roomJid nickname ->
- called <- true
- Seq.iter (fun h -> h (createSelfPresence roomJid nickname)) presenceHandlers
- )
- let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
- Lifetime.Using(fun lt ->
- Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo |> ignore
- Assert.True called
+let private createPresenceFor (roomJid: JID) nickname =
+ let presence = XMPPPresence()
+ let participantJid = JID(roomJid.FullJid)
+ participantJid.Resource <- nickname
+ presence.SetAttributeValue(From, participantJid.FullJid)
+ presence
+
+let private createSelfPresence roomJid nickname =
+ let presence = createPresenceFor roomJid nickname
+ let x = XElement X
+ let status = XElement Status
+ status.SetAttributeValue(Code, "110")
+ x.Add status
+ presence.Add x
+ presence
+
+let private createErrorPresence roomJid nickname errorXml =
+ let presence = createPresenceFor roomJid nickname
+ presence.SetAttributeValue(Type, "error")
+ let error = XElement Error
+ let errorChild = XElement.Parse errorXml
+ error.Add errorChild
+ presence.Add error
+ presence
+
+let private createLeavePresence roomJid nickname =
+ let presence = createSelfPresence roomJid nickname
+ presence.SetAttributeValue(Type, "unavailable")
+ presence
+
+let private sendPresence presence handlers =
+ Seq.iter (fun h -> h presence) handlers
+
+let private createErrorMessage (message: XMPPMessage) errorXml =
+ // An error message is an exact copy of the original with the "error" element added:
+ let errorMessage = XMPPMessage()
+ message.Attributes() |> Seq.iter (fun a -> errorMessage.SetAttributeValue(a.Name, a.Value))
+ message.Elements() |> Seq.iter (fun e -> errorMessage.Add e)
+
+ let error = XElement Error
+ let errorChild = XElement.Parse errorXml
+ error.Add errorChild
+ errorMessage.Add error
+ errorMessage
+
+[]
+let ``connect function calls the Connect method of the client passed``(): unit =
+ let mutable connectCalled = false
+ let client = XmppClientFactory.create(fun () -> async { connectCalled <- true })
+ Async.RunSynchronously <| XmppClient.connect client |> ignore
+ Assert.True connectCalled
+
+[]
+let ``connect function returns a lifetime terminated whenever the ConnectionFailed callback is triggered``()
+ : unit =
+ let mutable callback = ignore
+ let client = XmppClientFactory.create(addConnectionFailedHandler = fun _ h -> callback <- h)
+ let lt = Async.RunSynchronously <| XmppClient.connect client
+ Assert.True lt.IsAlive
+ callback(ConnFailedArgs())
+ Assert.False lt.IsAlive
+
+[]
+let ``enter function calls JoinMultiUserChat``(): unit =
+ let mutable called = false
+ let mutable presenceHandlers = ResizeArray()
+ let client =
+ XmppClientFactory.create(
+ addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
+ joinMultiUserChat = fun roomJid nickname ->
+ called <- true
+ Seq.iter (fun h -> h (createSelfPresence roomJid nickname)) presenceHandlers
)
-
- []
- member __.``enter throws an exception in case of an error presence``(): unit =
- let mutable presenceHandlers = ResizeArray()
- let client =
- XmppClientFactory.create(
- addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
- joinMultiUserChat = fun roomJid nickname ->
- sendPresence (createErrorPresence roomJid nickname "") presenceHandlers
- )
- let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
- Lifetime.Using(fun lt ->
- let ae = Assert.Throws(fun () ->
- Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo |> ignore
- )
- let ex = Seq.exactlyOne ae.InnerExceptions
- Assert.Contains("", ex.Message)
+ let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
+ Lifetime.Using(fun lt ->
+ Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo |> ignore
+ Assert.True called
+ )
+
+[]
+let ``enter throws an exception in case of an error presence``(): unit =
+ let mutable presenceHandlers = ResizeArray()
+ let client =
+ XmppClientFactory.create(
+ addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
+ joinMultiUserChat = fun roomJid nickname ->
+ sendPresence (createErrorPresence roomJid nickname "") presenceHandlers
)
-
- []
- member __.``Lifetime returned from enter terminates by a room leave presence``(): unit =
- let mutable presenceHandlers = ResizeArray()
- let client =
- XmppClientFactory.create(
- addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
- joinMultiUserChat = fun roomJid nickname ->
- sendPresence (createSelfPresence roomJid nickname) presenceHandlers
- )
- let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
- Lifetime.Using(fun lt ->
- let roomLt = Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo
- Assert.True roomLt.IsAlive
- sendPresence (createLeavePresence roomInfo.RoomJid roomInfo.Nickname) presenceHandlers
- Assert.False roomLt.IsAlive
+ let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
+ Lifetime.Using(fun lt ->
+ let ae = Assert.Throws(fun () ->
+ Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo |> ignore
)
-
- []
- member __.``Lifetime returned from enter terminates by an external lifetime termination``(): unit =
- let mutable presenceHandlers = ResizeArray()
- let client =
- XmppClientFactory.create(
- addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
- joinMultiUserChat = fun roomJid nickname ->
- sendPresence (createSelfPresence roomJid nickname) presenceHandlers
- )
- let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
- use ld = Lifetime.Define()
- let lt = ld.Lifetime
+ let ex = Seq.exactlyOne ae.InnerExceptions
+ Assert.Contains("", ex.Message)
+ )
+
+[]
+let ``Lifetime returned from enter terminates by a room leave presence``(): unit =
+ let mutable presenceHandlers = ResizeArray()
+ let client =
+ XmppClientFactory.create(
+ addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
+ joinMultiUserChat = fun roomJid nickname ->
+ sendPresence (createSelfPresence roomJid nickname) presenceHandlers
+ )
+ let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
+ Lifetime.Using(fun lt ->
let roomLt = Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo
Assert.True roomLt.IsAlive
- ld.Terminate()
+ sendPresence (createLeavePresence roomInfo.RoomJid roomInfo.Nickname) presenceHandlers
Assert.False roomLt.IsAlive
-
- []
- member __.``sendRoomMessage calls Send method on the client``(): unit =
- let mutable message = Unchecked.defaultof
- let client = XmppClientFactory.create(send = fun m -> message <- m)
- let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
- Lifetime.Using(fun lt ->
- Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo |> ignore
- Assert.Equal(messageInfo.RecipientJid.FullJid, message.To.FullJid)
- Assert.Equal(messageInfo.Text, message.Text)
- )
-
- []
- member __.``sendRoomMessage's result gets resolved after the message receival``(): unit =
- let mutable messageHandler = ignore
- let mutable message = Unchecked.defaultof
- let client =
- XmppClientFactory.create(
- addMessageHandler = (fun _ h -> messageHandler <- h),
- send = fun m -> message <- m
- )
- let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
- Lifetime.Using(fun lt ->
- let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
- Assert.Equal(message.ID, deliveryInfo.MessageId)
- let deliveryTask = Async.StartAsTask deliveryInfo.Delivery
- Assert.False deliveryTask.IsCompleted
- messageHandler message
- deliveryTask.Wait()
- )
-
- []
- member __.``sendRoomMessage's result doesn't get resolved after receiving other message``(): unit =
- let mutable messageHandler = ignore
- let client = XmppClientFactory.create(addMessageHandler = fun _ h -> messageHandler <- h)
- let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
- Lifetime.Using(fun lt ->
- let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
- let deliveryTask = Async.StartAsTask deliveryInfo.Delivery
- Assert.False deliveryTask.IsCompleted
-
- let otherMessage = SharpXmppHelper.message (Some "xxx") "nickname@example.org" "foo bar"
- messageHandler otherMessage
- Assert.False deliveryTask.IsCompleted
+ )
+
+[]
+let ``Lifetime returned from enter terminates by an external lifetime termination``(): unit =
+ let mutable presenceHandlers = ResizeArray()
+ let client =
+ XmppClientFactory.create(
+ addPresenceHandler = (fun _ h -> presenceHandlers.Add h),
+ joinMultiUserChat = fun roomJid nickname ->
+ sendPresence (createSelfPresence roomJid nickname) presenceHandlers
)
-
- []
- member __.``sendRoomMessage's result gets resolved with an error if an error response is received``(): unit =
- let mutable messageHandler = ignore
- let client =
- XmppClientFactory.create(
- addMessageHandler = (fun _ h -> messageHandler <- h),
- send = fun m -> messageHandler(createErrorMessage m "")
- )
- let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
- Lifetime.Using(fun lt ->
- let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
- let ae = Assert.Throws(fun () -> Async.RunSynchronously deliveryInfo.Delivery)
- let ex = Seq.exactlyOne ae.InnerExceptions
- Assert.Contains("", ex.Message)
+ let roomInfo = { RoomJid = JID("room@conference.example.org"); Nickname = "testuser" }
+ use ld = Lifetime.Define()
+ let lt = ld.Lifetime
+ let roomLt = Async.RunSynchronously <| XmppClient.enterRoom client lt roomInfo
+ Assert.True roomLt.IsAlive
+ ld.Terminate()
+ Assert.False roomLt.IsAlive
+
+[]
+let ``sendRoomMessage calls Send method on the client``(): unit =
+ let mutable message = Unchecked.defaultof
+ let client = XmppClientFactory.create(send = fun m -> message <- m)
+ let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
+ Lifetime.Using(fun lt ->
+ Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo |> ignore
+ Assert.Equal(messageInfo.RecipientJid.FullJid, message.To.FullJid)
+ Assert.Equal(messageInfo.Text, message.Text)
+ )
+
+[]
+let ``sendRoomMessage's result gets resolved after the message receival``(): unit =
+ let mutable messageHandler = ignore
+ let mutable message = Unchecked.defaultof
+ let client =
+ XmppClientFactory.create(
+ addMessageHandler = (fun _ h -> messageHandler <- h),
+ send = fun m -> message <- m
)
-
- []
- member __.``sendRoomMessage's result gets terminated after parent lifetime termination``(): unit =
- let client = XmppClientFactory.create()
- let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
- use ld = Lifetime.Define()
- let lt = ld.Lifetime
+ let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
+ Lifetime.Using(fun lt ->
+ let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
+ Assert.Equal(message.ID, deliveryInfo.MessageId)
+ let deliveryTask = Async.StartAsTask deliveryInfo.Delivery
+ Assert.False deliveryTask.IsCompleted
+ messageHandler message
+ deliveryTask.Wait()
+ )
+
+[]
+let ``sendRoomMessage's result doesn't get resolved after receiving other message``(): unit =
+ let mutable messageHandler = ignore
+ let client = XmppClientFactory.create(addMessageHandler = fun _ h -> messageHandler <- h)
+ let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
+ Lifetime.Using(fun lt ->
let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
let deliveryTask = Async.StartAsTask deliveryInfo.Delivery
Assert.False deliveryTask.IsCompleted
- ld.Terminate()
- Assert.Throws(fun () -> deliveryTask.GetAwaiter().GetResult()) |> ignore
-
- []
- member __.``awaitMessageDelivery just returns an async from the delivery info``(): unit =
- let async = async { return () }
- let deliveryInfo = { MessageId = ""; Delivery = async }
- let result = XmppClient.awaitMessageDelivery deliveryInfo
- Assert.True(Object.ReferenceEquals(async, result))
+
+ let otherMessage = SharpXmppHelper.message (Some "xxx") "nickname@example.org" "foo bar"
+ messageHandler otherMessage
+ Assert.False deliveryTask.IsCompleted
+ )
+
+[]
+let ``sendRoomMessage's result gets resolved with an error if an error response is received``(): unit =
+ let mutable messageHandler = ignore
+ let client =
+ XmppClientFactory.create(
+ addMessageHandler = (fun _ h -> messageHandler <- h),
+ send = fun m -> messageHandler(createErrorMessage m "")
+ )
+ let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
+ Lifetime.Using(fun lt ->
+ let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
+ let ae = Assert.Throws(fun () -> Async.RunSynchronously deliveryInfo.Delivery)
+ let ex = Seq.exactlyOne ae.InnerExceptions
+ Assert.Contains("", ex.Message)
+ )
+
+[]
+let ``sendRoomMessage's result gets terminated after parent lifetime termination``(): unit =
+ let client = XmppClientFactory.create()
+ let messageInfo = { RecipientJid = JID("room@conference.example.org"); Text = "foo bar" }
+ use ld = Lifetime.Define()
+ let lt = ld.Lifetime
+ let deliveryInfo = Async.RunSynchronously <| XmppClient.sendRoomMessage client lt messageInfo
+ let deliveryTask = Async.StartAsTask deliveryInfo.Delivery
+ Assert.False deliveryTask.IsCompleted
+ ld.Terminate()
+ Assert.Throws(fun () -> deliveryTask.GetAwaiter().GetResult()) |> ignore
+
+[]
+let ``awaitMessageDelivery just returns an async from the delivery info``(): unit =
+ let async = async { return () }
+ let deliveryInfo = { MessageId = ""; Delivery = async }
+ let result = XmppClient.awaitMessageDelivery deliveryInfo
+ Assert.True(Object.ReferenceEquals(async, result))
diff --git a/Emulsion/Xmpp/EmulsionXmpp.fs b/Emulsion/Xmpp/EmulsionXmpp.fs
index 3388b7dd..c198d8cd 100644
--- a/Emulsion/Xmpp/EmulsionXmpp.fs
+++ b/Emulsion/Xmpp/EmulsionXmpp.fs
@@ -1,5 +1,4 @@
/// Main business logic for an XMPP part of the Emulsion application.
-/// TODO[F]: Add tests for this module.
module Emulsion.Xmpp.EmulsionXmpp
open JetBrains.Lifetimes
@@ -42,7 +41,8 @@ let run (settings: XmppSettings)
(client: IXmppClient)
(messageReceiver: IncomingMessageReceiver): Async = async {
logger.Information "Connecting to the server"
- let! sessionLifetime = XmppClient.connect logger client
+ let! sessionLifetime = XmppClient.connect client
+ sessionLifetime.ThrowIfNotAlive()
logger.Information "Connection succeeded"
logger.Information "Initializing client handler"
diff --git a/Emulsion/Xmpp/SharpXmppHelper.fs b/Emulsion/Xmpp/SharpXmppHelper.fs
index 00fd53ee..450fe82c 100644
--- a/Emulsion/Xmpp/SharpXmppHelper.fs
+++ b/Emulsion/Xmpp/SharpXmppHelper.fs
@@ -105,7 +105,7 @@ let parsePresence(presence: XMPPPresence): Presence =
presence.Element X
|> Option.ofObj
|> Option.map (fun x ->
- x.Elements(Status)
+ x.Elements Status
|> Seq.choose (fun s -> getAttributeValue s Code)
|> Seq.map int
)
diff --git a/Emulsion/Xmpp/XmppClient.fs b/Emulsion/Xmpp/XmppClient.fs
index 4d3b6587..65b547a1 100644
--- a/Emulsion/Xmpp/XmppClient.fs
+++ b/Emulsion/Xmpp/XmppClient.fs
@@ -26,9 +26,9 @@ type IXmppClient =
/// Establish a connection to the server and log in. Returns a connection lifetime that will terminate if the connection
/// terminates.
-let connect (logger: ILogger) (client: IXmppClient): Async = async {
+let connect (client: IXmppClient): Async = async {
let connectionLifetime = new LifetimeDefinition()
- client.AddConnectionFailedHandler connectionLifetime.Lifetime <| fun error ->
+ client.AddConnectionFailedHandler connectionLifetime.Lifetime <| fun _ ->
connectionLifetime.Terminate()
do! client.Connect()