content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE DuplicateRecordFields #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE OverloadedLists #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE PatternSynonyms #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeApplications #-}
  {-# OPTIONS_GHC -Wno-orphans #-}
  {-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
  
  module AgentTests.ConnectionRequestTests
    ( connectionRequestTests,
      connReqData,
      queueAddr,
      testE2ERatchetParams12,
      contactConnRequest,
      invConnRequest,
    ) where
  
  import Data.ByteString (ByteString)
  import Network.HTTP.Types (urlEncode)
  import Simplex.Messaging.Agent.Protocol
  import qualified Simplex.Messaging.Crypto as C
  import Simplex.Messaging.Crypto.Ratchet
  import Simplex.Messaging.Encoding
  import Simplex.Messaging.Encoding.String
  import Simplex.Messaging.Protocol (EntityId (..), ProtocolServer (..), QueueMode (..), currentSMPClientVersion, supportedSMPClientVRange, pattern VersionSMPC)
  import Simplex.Messaging.ServiceScheme (ServiceScheme (..))
  import Simplex.Messaging.Version
  import Test.Hspec hiding (fit, it)
  import Util
  
  srv :: SMPServer
  srv = SMPServer "smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" "5223" (C.KeyHash "\215m\248\251")
  
  srv1 :: SMPServer
  srv1 = SMPServer "smp.simplex.im" "5223" (C.KeyHash "\215m\248\251")
  
  queueAddr :: SMPQueueAddress
  queueAddr =
    SMPQueueAddress
      { smpServer = srv,
        senderId = EntityId "\223\142z\251",
        dhPublicKey = testDhKey,
        queueMode = Just QMMessaging
      }
  
  queueAddrNoQM :: SMPQueueAddress
  queueAddrNoQM = queueAddr {queueMode = Nothing}
  
  queueAddrContact :: SMPQueueAddress
  queueAddrContact = queueAddr {queueMode = Just QMContact}
  
  queueAddr1 :: SMPQueueAddress
  queueAddr1 = queueAddr {smpServer = srv1}
  
  queueAddrNoPort :: SMPQueueAddress
  queueAddrNoPort = queueAddr {smpServer = srv {port = ""}}
  
  queueAddrNoPortNoQM :: SMPQueueAddress
  queueAddrNoPortNoQM = queueAddrNoQM {smpServer = srv {port = ""}}
  
  queueAddrNoPort1 :: SMPQueueAddress
  queueAddrNoPort1 = queueAddr {smpServer = srv1 {port = ""}}
  
  -- current version range includes version 1 and it uses legacy encoding
  queue :: SMPQueueUri
  queue = SMPQueueUri supportedSMPClientVRange queueAddr
  
  queueNoQM :: SMPQueueUri
  queueNoQM = SMPQueueUri supportedSMPClientVRange queueAddrNoQM
  
  queueContact :: SMPQueueUri
  queueContact = SMPQueueUri supportedSMPClientVRange queueAddrContact
  
  queueStr :: ByteString
  queueStr = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&q=m&k=s" <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion"
  
  queueStrNoQM :: ByteString
  queueStrNoQM = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion"
  
  queueStrContact :: ByteString
  queueStrContact = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&q=c" <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion"
  
  queue1 :: SMPQueueUri
  queue1 = SMPQueueUri supportedSMPClientVRange queueAddr1
  
  queue1Str :: ByteString
  queue1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&q=m&k=s"
  
  queueV1 :: SMPQueueUri
  queueV1 = SMPQueueUri (mkVersionRange (VersionSMPC 1) (VersionSMPC 1)) queueAddrNoQM
  
  queueV1NoPort :: SMPQueueUri
  queueV1NoPort = (queueV1 :: SMPQueueUri) {queueAddress = queueAddrNoPortNoQM}
  
  -- version range 2-3 uses new encoding
  -- it is fixed/changed in v5.8.2.
  queueNew :: SMPQueueUri
  queueNew = SMPQueueUri (mkVersionRange (VersionSMPC 2) currentSMPClientVersion) queueAddr
  
  queueNewStr :: ByteString
  queueNewStr = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr <> "&q=m&k=s"
  
  queueNewStr' :: ByteString
  queueNewStr' = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-4&dh=" <> testDhKeyStr <> "&q=m&k=s"
  
  queueNewNoPort :: SMPQueueUri
  queueNewNoPort = (queueNew :: SMPQueueUri) {queueAddress = queueAddrNoPort}
  
  queueNew1 :: SMPQueueUri
  queueNew1 = SMPQueueUri (mkVersionRange (VersionSMPC 2) currentSMPClientVersion) queueAddr1
  
  queueNew1Str :: ByteString
  queueNew1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr <> "&q=m&k=s"
  
  queueNew1NoPort :: SMPQueueUri
  queueNew1NoPort = (queueNew1 :: SMPQueueUri) {queueAddress = queueAddrNoPort1}
  
  testDhKey :: C.PublicKeyX25519
  testDhKey = "MCowBQYDK2VuAyEAjiswwI3O/NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o="
  
  testDhKeyStr :: ByteString
  testDhKeyStr = strEncode testDhKey
  
  connReqData :: ConnReqUriData
  connReqData =
    ConnReqUriData
      { crScheme = SSSimplex,
        crAgentVRange = supportedSMPAgentVRange,
        crSmpQueues = [queue],
        crClientData = Nothing
      }
  
  connReqDataNoQM :: ConnReqUriData
  connReqDataNoQM = connReqData {crSmpQueues = [queueNoQM]}
  
  connReqDataContact :: ConnReqUriData
  connReqDataContact = connReqData {crSmpQueues = [queueContact]}
  
  connReqData1 :: ConnReqUriData
  connReqData1 = connReqData {crSmpQueues = [queue1]}
  
  connReqDataV1 :: ConnReqUriData
  connReqDataV1 = connReqData {crAgentVRange = mkVersionRange (VersionSMPA 1) (VersionSMPA 1)}
  
  connReqDataV2 :: ConnReqUriData
  connReqDataV2 = connReqData {crAgentVRange = mkVersionRange (VersionSMPA 2) (VersionSMPA 2)}
  
  connReqDataNew :: ConnReqUriData
  connReqDataNew = connReqData {crSmpQueues = [queueNew]}
  
  connReqDataNew1 :: ConnReqUriData
  connReqDataNew1 = connReqData {crSmpQueues = [queueNew1]}
  
  testDhPubKey :: C.PublicKeyX448
  testDhPubKey = "MEIwBQYDK2VvAzkAmKuSYeQ/m0SixPDS8Wq8VBaTS1cW+Lp0n0h4Diu+kUpR+qXx4SDJ32YGEFoGFGSbGPry5Ychr6U="
  
  testE2ERatchetParams :: RcvE2ERatchetParamsUri 'C.X448
  testE2ERatchetParams = E2ERatchetParamsUri (mkVersionRange (VersionE2E 1) (VersionE2E 1)) testDhPubKey testDhPubKey Nothing
  
  testE2ERatchetParamsStrUri :: ByteString
  testE2ERatchetParamsStrUri = "v%3D1%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
  
  testE2ERatchetParams12 :: RcvE2ERatchetParamsUri 'C.X448
  testE2ERatchetParams12 = E2ERatchetParamsUri supportedE2EEncryptVRange testDhPubKey testDhPubKey Nothing
  
  connectionRequest :: AConnectionRequestUri
  connectionRequest = ACR SCMInvitation invConnRequest
  
  invConnRequest :: ConnectionRequestUri 'CMInvitation
  invConnRequest = CRInvitationUri connReqData testE2ERatchetParams
  
  connectionRequestNoQM :: AConnectionRequestUri
  connectionRequestNoQM = ACR SCMInvitation $ CRInvitationUri connReqDataNoQM testE2ERatchetParams
  
  connectionRequestContact :: AConnectionRequestUri
  connectionRequestContact = ACR SCMContact $ CRContactUri connReqDataContact
  
  connectionRequestV1 :: AConnectionRequestUri
  connectionRequestV1 = ACR SCMInvitation $ CRInvitationUri connReqDataV1 testE2ERatchetParams
  
  connectionRequest1 :: AConnectionRequestUri
  connectionRequest1 = ACR SCMInvitation $ CRInvitationUri connReqData1 testE2ERatchetParams
  
  connectionRequestNew :: AConnectionRequestUri
  connectionRequestNew = ACR SCMInvitation $ CRInvitationUri connReqDataNew testE2ERatchetParams
  
  connectionRequestNew1 :: AConnectionRequestUri
  connectionRequestNew1 = ACR SCMInvitation $ CRInvitationUri connReqDataNew1 testE2ERatchetParams
  
  contactAddress :: AConnectionRequestUri
  contactAddress = ACR SCMContact $ contactConnRequest
  
  contactConnRequest :: ConnectionRequestUri 'CMContact
  contactConnRequest = CRContactUri connReqData
  
  contactAddressV2 :: AConnectionRequestUri
  contactAddressV2 = ACR SCMContact $ CRContactUri connReqDataV2
  
  contactAddressNew :: AConnectionRequestUri
  contactAddressNew = ACR SCMContact $ CRContactUri connReqDataNew
  
  connectionRequest2queues :: AConnectionRequestUri
  connectionRequest2queues = ACR SCMInvitation $ CRInvitationUri connReqData {crSmpQueues = [queue, queue]} testE2ERatchetParams
  
  connectionRequest2queuesNew :: AConnectionRequestUri
  connectionRequest2queuesNew = ACR SCMInvitation $ CRInvitationUri connReqDataNew {crSmpQueues = [queueNew, queueNew]} testE2ERatchetParams
  
  contactAddress2queues :: AConnectionRequestUri
  contactAddress2queues = ACR SCMContact $ CRContactUri connReqData {crSmpQueues = [queue, queue]}
  
  contactAddress2queuesNew :: AConnectionRequestUri
  contactAddress2queuesNew = ACR SCMContact $ CRContactUri connReqDataNew {crSmpQueues = [queueNew, queueNew]}
  
  connectionRequestClientDataEmpty :: AConnectionRequestUri
  connectionRequestClientDataEmpty = ACR SCMInvitation $ CRInvitationUri connReqData {crClientData = Just "{}"} testE2ERatchetParams
  
  contactAddressClientData :: AConnectionRequestUri
  contactAddressClientData = ACR SCMContact $ CRContactUri connReqData {crClientData = Just "{\"type\":\"group_link\", \"group_link_id\":\"abc\"}"}
  
  url :: ByteString -> ByteString
  url = urlEncode True
  
  (==#) :: (StrEncoding a, HasCallStack) => a -> ByteString -> Expectation
  a ==# s = strEncode a `shouldBe` s
  
  (#==) :: (StrEncoding a, Eq a, Show a, HasCallStack) => a -> ByteString -> Expectation
  a #== s = strDecode s `shouldBe` Right a
  
  (#==#) :: (StrEncoding a, Eq a, Show a, HasCallStack) => a -> ByteString -> Expectation
  a #==# s = do
    a ==# s
    a #== s
  
  connectionRequestTests :: Spec
  connectionRequestTests =
    describe "connection request parsing / serializing" $ do
      it "should serialize and parse SMP queue URIs" $ do
        queue #==# queueStr
        queue #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#" <> testDhKeyStr <> "/?v=1-4&extra_param=abc&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&q=m&k=s")
        queueNoQM #==# queueStrNoQM
        queueContact #==# queueStrContact
        queue1 #==# queue1Str
        queueNew #==# queueNewStr
        queueNew #== queueNewStr'
        queueNew1 #==# queueNew1Str
        queueNewNoPort #==# ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr <> "&q=m&k=s")
        queueNew1NoPort #==# ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr <> "&q=m&k=s")
        queueV1 #==# ("smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
        queueV1 #== ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#" <> testDhKeyStr)
        queueV1 #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#/?extra_param=abc&v=1&dh=" <> testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
        queueV1 #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#" <> testDhKeyStr <> "/?v=1&extra_param=abc&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
        queueV1NoPort #==# ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=1&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
        queueV1NoPort #== ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=1-1&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
        queueV1NoPort #== ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion/3456-w==#" <> testDhKeyStr)
      it "should serialize and parse connection invitations and contact addresses" $ do
        connectionRequest #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequest #== ("https://simplex.chat/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequestNoQM #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStrNoQM <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequest1 #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queue1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequest2queues #==# ("simplex:/invitation#/?v=2-7&smp=" <> url (queueStr <> ";" <> queueStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequestNew #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueNewStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequestNew1 #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueNew1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequest2queuesNew #==# ("simplex:/invitation#/?v=2-7&smp=" <> url (queueNewStr <> ";" <> queueNewStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequestV1 #== ("https://simplex.chat/invitation#/?v=1&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
        connectionRequestClientDataEmpty #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri <> "&data=" <> url "{}")
        contactAddress #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueStr)
        contactAddress #== ("https://simplex.chat/contact#/?v=2-7&smp=" <> url queueStr)
        contactAddress2queues #==# ("simplex:/contact#/?v=2-7&smp=" <> url (queueStr <> ";" <> queueStr))
        contactAddressNew #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueNewStr)
        contactAddress2queuesNew #==# ("simplex:/contact#/?v=2-7&smp=" <> url (queueNewStr <> ";" <> queueNewStr))
        contactAddressV2 #==# ("simplex:/contact#/?v=2&smp=" <> url queueStr)
        contactAddressV2 #== ("https://simplex.chat/contact#/?v=1&smp=" <> url queueStr) -- adjusted to v2
        contactAddressV2 #== ("https://simplex.chat/contact#/?v=1-2&smp=" <> url queueStr) -- adjusted to v2
        contactAddressV2 #== ("https://simplex.chat/contact#/?v=2-2&smp=" <> url queueStr)
        contactAddressClientData #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueStr <> "&data=" <> url "{\"type\":\"group_link\", \"group_link_id\":\"abc\"}")
      it "should serialize / parse queue address, connection invitations and contact addresses as binary" $ do
        smpEncodingTest queue
        smpEncodingTest queueNoQM -- this passes, no queue mode patch in SMPQueueUri encoding
        -- smpEncodingTest queueContact -- this fails until SMP client min version is >= sndAuthKeySMPClientVersion
        smpEncodingTest queue1
        smpEncodingTest queueNew
        smpEncodingTest queueNew1
        smpEncodingTest queueNewNoPort
        smpEncodingTest queueNew1NoPort
        smpEncodingTest queueV1
        smpEncodingTest queueV1NoPort
        smpEncodingTest connectionRequest
        -- smpEncodingTest connectionRequestNoQM -- this fails, because of queue mode patch
        smpEncodingTest connectionRequestContact -- this passes because of queue mode patch in ConnReqUriData encoding
        smpEncodingTest connectionRequest1
        smpEncodingTest connectionRequest2queues
        smpEncodingTest connectionRequestNew
        smpEncodingTest connectionRequestNew1
        smpEncodingTest connectionRequest2queuesNew
        smpEncodingTest connectionRequestClientDataEmpty
        smpEncodingTest contactAddress
        smpEncodingTest contactAddress2queues
        smpEncodingTest contactAddressNew
        smpEncodingTest contactAddress2queuesNew
        smpEncodingTest contactAddressV2
        smpEncodingTest contactAddressClientData
      it "should serialize / parse short links" $ do
        CSLContact SLSServer CCTContact srv (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/a#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLContact SLSServer CCTGroup srv (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/g#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLContact SLSServer CCTContact shortSrv (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/a#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY"
        CSLInvitation SLSServer srv (EntityId "0123456789abcdef01234567") (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/i#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3/MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLInvitation SLSServer shortSrv (EntityId "0123456789abcdef01234567") (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/i#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3/MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY"
        CSLContact SLSSimplex CCTContact srv (LinkKey "0123456789abcdef0123456789abcdef") #==# "simplex:/a#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=smp.simplex.im%2Cjjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLContact SLSSimplex CCTGroup srv (LinkKey "0123456789abcdef0123456789abcdef") #==# "simplex:/g#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=smp.simplex.im%2Cjjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLContact SLSSimplex CCTContact shortSrv (LinkKey "0123456789abcdef0123456789abcdef") #==# "simplex:/a#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=smp.simplex.im"
        CSLInvitation SLSSimplex srv (EntityId "0123456789abcdef01234567") (LinkKey "0123456789abcdef0123456789abcdef") #==# "simplex:/i#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3/MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=smp.simplex.im%2Cjjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
        CSLInvitation SLSSimplex shortSrv (EntityId "0123456789abcdef01234567") (LinkKey "0123456789abcdef0123456789abcdef") #==# "simplex:/i#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3/MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=smp.simplex.im"
      it "should shorten / restore short links" $ do
        let contact = CSLContact SLSServer CCTContact
        shortenShortLink [srv] (contact srv (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact shortSrv (LinkKey "0123456789abcdef0123456789abcdef")
        -- won't shorten link that uses only onion host from preset server
        shortenShortLink [srv] (contact srvOnion (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact srvOnion (LinkKey "0123456789abcdef0123456789abcdef")
        -- will shorten link that uses only public host from preset server
        shortenShortLink [srv] (contact srv1 (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact shortSrv (LinkKey "0123456789abcdef0123456789abcdef")
        shortenShortLink [srv] (contact srv2 (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact srv2 (LinkKey "0123456789abcdef0123456789abcdef")
        restoreShortLink [srv] (contact shortSrv (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact srv (LinkKey "0123456789abcdef0123456789abcdef")
        -- won't change link that has only public host of preset server with keyhash
        restoreShortLink [srv] (contact srv1 (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact srv1 (LinkKey "0123456789abcdef0123456789abcdef")
        restoreShortLink [srv2] (contact shortSrv (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact shortSrv (LinkKey "0123456789abcdef0123456789abcdef")
        restoreShortLink [srv] (contact srv2 (LinkKey "0123456789abcdef0123456789abcdef"))
          `shouldBe` contact srv2 (LinkKey "0123456789abcdef0123456789abcdef")
        Right (lnk :: ConnShortLink 'CMContact) <- pure $ strDecode "https://localhost/a#4AkRDmhf64tdRlN406g8lJRg5OCmhD6ynIhi6glOcCM?p=7001&c=LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI"
        Right (lnk' :: ConnShortLink 'CMContact) <- pure $ strDecode "https://localhost/a#4AkRDmhf64tdRlN406g8lJRg5OCmhD6ynIhi6glOcCM"
        let presetSrv :: SMPServer = "smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@localhost:7001"
        shortenShortLink [presetSrv] lnk `shouldBe` lnk'
        restoreShortLink [presetSrv] lnk' `shouldBe` lnk
        Right (inv :: ConnShortLink 'CMInvitation) <- pure $ strDecode "https://localhost/i#tnUaHYp8saREmyEHR93SBpl8ySHBchOt/LJ1ZQUzxH9Udb0jw5wmJACv5o6oe8e7BsX_hUCUMTSY?p=7001&c=LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI"
        Right (inv' :: ConnShortLink 'CMInvitation) <- pure $ strDecode "https://localhost/i#tnUaHYp8saREmyEHR93SBpl8ySHBchOt/LJ1ZQUzxH9Udb0jw5wmJACv5o6oe8e7BsX_hUCUMTSY"
        shortenShortLink [presetSrv] inv `shouldBe` inv'
        restoreShortLink [presetSrv] inv' `shouldBe` inv
    where
      smpEncodingTest :: (Encoding a, Eq a, Show a, HasCallStack) => a -> Expectation
      smpEncodingTest a = smpDecode (smpEncode a) `shouldBe` Right a
  
  shortSrv :: SMPServer
  shortSrv = SMPServer "smp.simplex.im" "" (C.KeyHash "")
  
  srvOnion :: SMPServer
  srvOnion = SMPServer "jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" "" (C.KeyHash "\215m\248\251")
  
  srv2 :: SMPServer
  srv2 = SMPServer "smp2.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" "" (C.KeyHash "\215m\248\251")
  