mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 02:12:25 +00:00
Merge branch 'master' into badge-in-app-checkout
This commit is contained in:
commit
57ab333a2b
94 files changed
+2255
-405
No files matched your search
@@ -60,6 +60,7 @@ badgeTests = do
|
||||
it "should accept unknown badge types" testUnknownBadgeType
|
||||
it "credential serializes to a paste-able token and back" testCredentialSerialization
|
||||
it "presentation headers encode and decode" testPresHeaderEncoding
|
||||
it "presentation headers with binary bytes round-trip through JSON" testPresHeaderJSON
|
||||
it "should reject a proof presented under another chat binding" testOtherChatBinding
|
||||
describe "redemption codes" $ do
|
||||
it "a generated code reads back" testCodeRoundTrip
|
||||
@@ -227,6 +228,21 @@ testPresHeaderEncoding =
|
||||
PHUnknown 'Z' "payload"
|
||||
]
|
||||
|
||||
testPresHeaderJSON :: IO ()
|
||||
testPresHeaderJSON =
|
||||
mapM_
|
||||
( \ph -> do
|
||||
J.eitherDecode (J.encode ph) `shouldBe` Right ph
|
||||
J.toJSON ph `shouldBe` J.toJSON (BBSPresHeader $ strEncode ph)
|
||||
)
|
||||
[ PHChat binaryBytes,
|
||||
PHFileInv {chatBinding = binaryBytes, fileSize = 139737},
|
||||
PHFileDescr {chatBinding = binaryBytes, fileSize = 139737, descrHash = binaryBytes, fileExpires = Just futureTime},
|
||||
PHUnknown 'Z' binaryBytes
|
||||
]
|
||||
where
|
||||
binaryBytes = "\0\1\31\34\92\127\128\195\255"
|
||||
|
||||
testOtherChatBinding :: IO ()
|
||||
testOtherChatBinding = do
|
||||
let ph = PHFileInv {chatBinding = aliceBinding, fileSize = 139737}
|
||||
|
||||
+1
-1
@@ -641,7 +641,7 @@ smpServerCfg p =
|
||||
maxJournalMsgCount = 24,
|
||||
maxJournalStateLines = 4,
|
||||
queueIdBytes = 24,
|
||||
msgIdBytes = 6,
|
||||
msgIdBytes = 24,
|
||||
serverStoreCfg = SSCMemory Nothing, -- $ Just StorePaths {storeLogFile = "tmp/smp-server-store.log", storeMsgsFile = Just "tmp/smp-server-messages.log"},
|
||||
storeNtfsFile = Nothing,
|
||||
allowNewQueues = True,
|
||||
|
||||
@@ -1158,6 +1158,13 @@ testGetSetSMPServers =
|
||||
alice <## " SMP servers"
|
||||
alice <## " smp://2345-w==@smp2.example.im"
|
||||
alice <## " smp://3456-w==@smp3.example.im:5224"
|
||||
alice #$> ("/smp smp://2345-w==@smp2.example.im,smp4.example.im", id, "ok")
|
||||
alice ##> "/smp smp://2345-w==@smp2.example.im,smp4.example.im,smp5.example.im"
|
||||
alice <##. "bad chat command: user servers validation error(s): [USETooManyHosts"
|
||||
alice ##> "/smp"
|
||||
alice <## "Your servers"
|
||||
alice <## " SMP servers"
|
||||
alice <## " smp://2345-w==@smp2.example.im,smp4.example.im"
|
||||
|
||||
testTestSMPServerConnection :: HasCallStack => TestParams -> IO ()
|
||||
testTestSMPServerConnection =
|
||||
|
||||
@@ -38,7 +38,7 @@ import Simplex.Chat.Messages (CIMention (..), CIMentionMember (..), ChatItemId)
|
||||
import Simplex.Chat.Messages.Batch (encodeBinaryBatch, encodeFwdElement)
|
||||
import Simplex.Chat.Messages.CIContent (publicGroupNoE2EText)
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XMsgUpdate, XMsgNew, XMsgDel), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), mcSimple, msgContentText)
|
||||
import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XInfo, XMsgUpdate, XMsgNew, XMsgDel), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), mcSimple, msgContentText)
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.MemberRelations (MemberRelation (..), getRelation, setRelation)
|
||||
import Simplex.Chat.Types.Shared (GroupMemberRole (..), GroupAcceptance (..))
|
||||
@@ -119,6 +119,7 @@ chatGroupTests = do
|
||||
xit "create and join group when clients go offline" testGroupAsync
|
||||
describe "group links" $ do
|
||||
it "create group link, join via group link" testGroupLink
|
||||
it "join via group link with group profile near the size limit" testGroupLinkLargeGroupProfile
|
||||
it "invitees were previously connected as contacts" testGroupLinkInviteesWereConnected
|
||||
it "all members were previously connected as contacts" testGroupLinkAllMembersWereConnected
|
||||
it "delete group, re-join via same link" testGroupLinkDeleteGroupRejoin
|
||||
@@ -176,6 +177,7 @@ chatGroupTests = do
|
||||
it "manually accept contact with group member incognito" testMemberContactAcceptIncognito
|
||||
describe "group message forwarding" $ do
|
||||
it "forward messages between invitee and introduced (x.msg.new)" testGroupMsgForwardMessage
|
||||
it "reject forwarded content attributed to own membership" testGroupMsgForwardOwnMembershipRejected
|
||||
it "forward batched messages" testGroupMsgForwardBatched
|
||||
it "forward reports to moderators, don't forward to members (x.msg.new, MCReport)" testGroupMsgForwardReport
|
||||
it "deduplicate forwarded messages" testGroupMsgForwardDeduplicate
|
||||
@@ -3042,6 +3044,39 @@ testPlanGroupLinkLeaveRejoin =
|
||||
bob <## "group link: known group #team_1"
|
||||
bob <## "use #team_1 <message> to send messages"
|
||||
|
||||
testGroupLinkLargeGroupProfile :: HasCallStack => TestParams -> IO ()
|
||||
testGroupLinkLargeGroupProfile =
|
||||
testChatOpts2 testOptsNoFullLinks aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
threadDelay 100000
|
||||
alice ##> "/g team"
|
||||
alice <## "group #team is created"
|
||||
alice <## "to add members use /a team <name> or /create link #team"
|
||||
alice ##> "/set history #team off"
|
||||
alice <## "updated group preferences:"
|
||||
alice <## "Recent history: off"
|
||||
withCCTransaction alice $ \db ->
|
||||
DB.execute db "UPDATE group_profiles SET description = ? WHERE display_name = ?" (T.pack welcome, "team" :: T.Text)
|
||||
alice ##> "/create link #team"
|
||||
gLink <- getGroupLink_ alice "team" GRMember True
|
||||
alice <// 100000
|
||||
bob ##> ("/c " <> gLink)
|
||||
bob <## "connection request sent!"
|
||||
alice <## "bob (Bob): accepting request to join group #team..."
|
||||
concurrentlyN_
|
||||
[ alice <## "#team: bob joined the group",
|
||||
do
|
||||
bob <## "#team: joining the group..."
|
||||
bob <## "#team: you joined the group"
|
||||
]
|
||||
alice #> "#team hello"
|
||||
let waitHello = do
|
||||
l <- getTermLine bob
|
||||
if "alice> hello" `isSuffixOf` l then pure () else waitHello
|
||||
waitHello
|
||||
where
|
||||
welcome = unwords $ replicate 1775 "welcome"
|
||||
|
||||
testGroupLink :: HasCallStack => TestParams -> IO ()
|
||||
testGroupLink =
|
||||
testChatOpts3 testOptsNoFullLinks aliceProfile bobProfile cathProfile $
|
||||
@@ -5399,6 +5434,28 @@ testGroupMsgForwardMessage =
|
||||
cath <# "#team bob> hi there [>>]"
|
||||
cath <# "#team hey team"
|
||||
|
||||
testGroupMsgForwardOwnMembershipRejected :: HasCallStack => TestParams -> IO ()
|
||||
testGroupMsgForwardOwnMembershipRejected =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
createGroup2 "team" alice bob
|
||||
threadDelay 1000000
|
||||
[Only bobMemId] <- withCCTransaction bob $ \db ->
|
||||
DB.query db "SELECT member_id FROM group_members WHERE member_category = ?" (Only ("user" :: T.Text)) :: IO [Only ByteString]
|
||||
connId <- relayConnIdToMember alice "bob"
|
||||
ts <- getCurrentTime
|
||||
let ChatController {smpAgent = aliceAgent} = chatController alice
|
||||
forgedProfile = (bobProfile :: Profile) {displayName = "mallory", fullName = "Mallory"}
|
||||
chatMsg = ChatMessage chatInitialVRange (Just $ SharedMsgId "forged_info1") (XInfo forgedProfile Nothing)
|
||||
fwd = GrpMsgForward (FwdMember (MemberId bobMemId) "bob") ts
|
||||
body = encodeBinaryBatch [encodeFwdElement fwd (VMUnsigned chatMsg)]
|
||||
sent <- runExceptT $ sendMessages aliceAgent [(connId, PQEncOff, MsgFlags False, vrValue body)]
|
||||
either (fail . show) (const $ pure ()) sent
|
||||
bob <##. "error: x.grp.msg.forward: content attributed to own membership"
|
||||
bob ##> "/p"
|
||||
bob <## "user profile: bob (Bob)"
|
||||
bob <## "use /p <name> [<bio>] to change it"
|
||||
|
||||
testGroupMsgForwardBatched :: HasCallStack => TestParams -> IO ()
|
||||
testGroupMsgForwardBatched =
|
||||
testChat3 aliceProfile bobProfile cathProfile $
|
||||
@@ -7979,8 +8036,8 @@ testForwardQuoteMention =
|
||||
bob <# "alice> -> forwarded"
|
||||
bob <## " hello @alice @cath"
|
||||
-- member renamed to duplicate name
|
||||
cath ##> "/p alice_1"
|
||||
cath <## "user profile is changed to alice_1 (your 1 contacts are notified)"
|
||||
cath ##> "/p alice"
|
||||
cath <## "user profile is changed to alice (your 1 contacts are notified)"
|
||||
alice <## "contact cath changed to alice_1"
|
||||
alice <## "use @alice_1 <message> to send messages"
|
||||
-- mention changed in quoted mentions
|
||||
|
||||
@@ -16,6 +16,9 @@ import Test.Hspec hiding (it)
|
||||
chatNamesTests :: SpecWith TestParams
|
||||
chatNamesTests = do
|
||||
it "connect by resolved name" testConnectByName
|
||||
it "profile update without the name keeps the name" testUpdateProfileKeepsName
|
||||
it "stopping address sharing removes the name" testAddressSharingOffRemovesName
|
||||
it "deleting the address removes the name" testAddressDeleteRemovesName
|
||||
it "connect by name not claimed in link profile is rejected" testConnectByNameNotClaimed
|
||||
it "connect by name to a known contact not claimed in profile is rejected" testConnectByNameKnownContactNotClaimed
|
||||
it "connect by unregistered name fails to resolve" testConnectByNameNotFound
|
||||
@@ -62,6 +65,123 @@ testConnectByName ps = withSmpServerAndNames ps $ \reg ->
|
||||
_ <- getTermLine bob
|
||||
pure ()
|
||||
|
||||
testUpdateProfileKeepsName :: HasCallStack => TestParams -> IO ()
|
||||
testUpdateProfileKeepsName ps = withSmpServerAndNames ps $ \reg ->
|
||||
testChat2 aliceProfile bobProfile (test reg) ps
|
||||
where
|
||||
aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" [])
|
||||
test reg alice bob = do
|
||||
mapM_ enableNamesRole [alice, bob]
|
||||
alice ##> "/ad"
|
||||
(shortLink, _) <- getContactLinks alice True
|
||||
registerName reg aliceName (contactNameRecord "alice.simplex" (T.pack shortLink))
|
||||
alice ##> "/_set domain 1 alice.simplex"
|
||||
alice <## "new contact address set"
|
||||
bob ##> "/c @alice.simplex"
|
||||
bob <## "alice: connection started"
|
||||
alice <## "bob (Bob) wants to connect to you!"
|
||||
alice <## "to accept: /ac bob"
|
||||
alice <## "to reject: /rc bob (the sender will NOT be notified)"
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice ##> ("/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"\", \"shortDescr\": \"new bio\", \"contactLink\": \"" <> shortLink <> "\"}")
|
||||
alice <## "user bio changed to new bio (your 1 contacts are notified)"
|
||||
bob <## "contact alice updated bio: new bio"
|
||||
bob ##> "/i alice"
|
||||
bob <## "contact ID: 2"
|
||||
bob <## "receiving messages via: localhost"
|
||||
bob <## "sending messages via: localhost"
|
||||
_ <- getTermLine bob
|
||||
bob <## "SimpleX name: @alice.simplex (verified)"
|
||||
bob <## "you've shared main profile with this contact"
|
||||
bob <## "connection not verified, use /code command to see security code"
|
||||
bob <## "quantum resistant end-to-end encryption"
|
||||
_ <- getTermLine bob
|
||||
pure ()
|
||||
|
||||
testAddressSharingOffRemovesName :: HasCallStack => TestParams -> IO ()
|
||||
testAddressSharingOffRemovesName ps = withSmpServerAndNames ps $ \reg ->
|
||||
testChat2 aliceProfile bobProfile (test reg) ps
|
||||
where
|
||||
aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" [])
|
||||
test reg alice bob = do
|
||||
mapM_ enableNamesRole [alice, bob]
|
||||
alice ##> "/ad"
|
||||
(shortLink, _) <- getContactLinks alice True
|
||||
registerName reg aliceName (contactNameRecord "alice.simplex" (T.pack shortLink))
|
||||
alice ##> "/_set domain 1 alice.simplex"
|
||||
alice <## "new contact address set"
|
||||
bob ##> "/c @alice.simplex"
|
||||
bob <## "alice: connection started"
|
||||
alice <## "bob (Bob) wants to connect to you!"
|
||||
alice <## "to accept: /ac bob"
|
||||
alice <## "to reject: /rc bob (the sender will NOT be notified)"
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice ##> "/pa off"
|
||||
alice <## "contact address removed"
|
||||
bob <## "alice removed contact address"
|
||||
checkNoName bob
|
||||
alice ##> "/pa on"
|
||||
alice <## "new contact address set"
|
||||
bob <## "alice set new contact address, use /info alice to view"
|
||||
bob ##> "/i alice"
|
||||
bob <## "contact ID: 2"
|
||||
bob <## "receiving messages via: localhost"
|
||||
bob <## "sending messages via: localhost"
|
||||
_ <- getTermLine bob
|
||||
bob <## "you've shared main profile with this contact"
|
||||
bob <## "connection not verified, use /code command to see security code"
|
||||
bob <## "quantum resistant end-to-end encryption"
|
||||
_ <- getTermLine bob
|
||||
pure ()
|
||||
|
||||
testAddressDeleteRemovesName :: HasCallStack => TestParams -> IO ()
|
||||
testAddressDeleteRemovesName ps = withSmpServerAndNames ps $ \reg ->
|
||||
testChat2 aliceProfile bobProfile (test reg) ps
|
||||
where
|
||||
aliceName = SimplexNameInfo NTContact (SimplexDomain TLDSimplex "alice" [])
|
||||
test reg alice bob = do
|
||||
mapM_ enableNamesRole [alice, bob]
|
||||
alice ##> "/ad"
|
||||
(shortLink, _) <- getContactLinks alice True
|
||||
registerName reg aliceName (contactNameRecord "alice.simplex" (T.pack shortLink))
|
||||
alice ##> "/_set domain 1 alice.simplex"
|
||||
alice <## "new contact address set"
|
||||
bob ##> "/c @alice.simplex"
|
||||
bob <## "alice: connection started"
|
||||
alice <## "bob (Bob) wants to connect to you!"
|
||||
alice <## "to accept: /ac bob"
|
||||
alice <## "to reject: /rc bob (the sender will NOT be notified)"
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice ##> "/da"
|
||||
alice <## "Your chat address is deleted - accepted contacts will remain connected."
|
||||
alice <## "To create a new chat address use /ad"
|
||||
bob <## "alice removed contact address"
|
||||
checkNoName bob
|
||||
|
||||
checkNoName :: HasCallStack => TestCC -> IO ()
|
||||
checkNoName bob = do
|
||||
bob ##> "/i alice"
|
||||
bob <## "contact ID: 2"
|
||||
bob <## "receiving messages via: localhost"
|
||||
bob <## "sending messages via: localhost"
|
||||
bob <## "you've shared main profile with this contact"
|
||||
bob <## "connection not verified, use /code command to see security code"
|
||||
bob <## "quantum resistant end-to-end encryption"
|
||||
_ <- getTermLine bob
|
||||
pure ()
|
||||
|
||||
testConnectByNameNotClaimed :: HasCallStack => TestParams -> IO ()
|
||||
testConnectByNameNotClaimed ps = withSmpServerAndNames ps $ \reg ->
|
||||
testChat2 aliceProfile bobProfile (test reg) ps
|
||||
|
||||
@@ -46,6 +46,10 @@ chatProfileTests :: SpecWith TestParams
|
||||
chatProfileTests = do
|
||||
describe "user profiles" $ do
|
||||
it "update user profile and notify contacts" testUpdateProfile
|
||||
it "profile is not saved when its address data upload fails" testUpdateProfileAddressDataError
|
||||
it "profile name used by a contact gets a local name with suffix" testUpdateProfileNameUsedByContact
|
||||
it "profile name used by another user is rejected, names with _N suffix are invalid" testUpdateProfileNameUsedByUser
|
||||
it "profile name used by a hidden user is rejected" testUpdateProfileNameUsedByHiddenUser
|
||||
it "profile description round-trips and shows in contact info" testProfileDescriptionShown
|
||||
it "member profile description is redacted for members without a direct contact" testMemberDescriptionRedacted
|
||||
it "update user profile with image" testUpdateProfileImage
|
||||
@@ -210,6 +214,117 @@ testUpdateProfile =
|
||||
bob <## "use @cat <message> to send messages"
|
||||
]
|
||||
|
||||
testUpdateProfileAddressDataError :: HasCallStack => TestParams -> IO ()
|
||||
testUpdateProfileAddressDataError ps = do
|
||||
let cfg' = testCfg {agentConfig = testAgentCfg {persistErrorInterval = 0}}
|
||||
withNewTestChatCfgOpts ps cfg' opts' "alice" aliceProfile $ \alice ->
|
||||
withNewTestChatCfgOpts ps cfg' opts' "bob" bobProfile $ \bob -> do
|
||||
withSmpServer' serverCfg' $ do
|
||||
connectUsers alice bob
|
||||
alice ##> "/ad"
|
||||
_ <- getContactLinks alice True
|
||||
pure ()
|
||||
alice <## "disconnected 2 connections on server localhost"
|
||||
bob <## "disconnected 1 connections on server localhost"
|
||||
alice ##> "/p alisa"
|
||||
alice <##. "smp agent error: BROKER"
|
||||
alice ##> "/p"
|
||||
alice <## "user profile: alice (Alice)"
|
||||
alice <## "use /p <name> [<bio>] to change it"
|
||||
withSmpServer' serverCfg' $ do
|
||||
alice <## "subscribed 2 connections on server localhost"
|
||||
bob <## "subscribed 1 connections on server localhost"
|
||||
alice ##> "/p alisa"
|
||||
alice <## "user profile is changed to alisa (your 1 contacts are notified)"
|
||||
bob <## "contact alice changed to alisa"
|
||||
bob <## "use @alisa <message> to send messages"
|
||||
alice <## "disconnected 2 connections on server localhost"
|
||||
bob <## "disconnected 1 connections on server localhost"
|
||||
where
|
||||
serverCfg' =
|
||||
(smpServerCfg ps)
|
||||
{ transports = [(smpTestPort2 ps, transport @TLS, False)],
|
||||
serverStoreCfg = persistentServerStoreCfg (tmpPath ps)
|
||||
}
|
||||
opts' =
|
||||
testOpts
|
||||
{ coreOptions =
|
||||
testCoreOpts
|
||||
{ smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7003"]
|
||||
}
|
||||
}
|
||||
|
||||
testUpdateProfileNameUsedByContact :: HasCallStack => TestParams -> IO ()
|
||||
testUpdateProfileNameUsedByContact =
|
||||
testChat3 aliceProfile bobProfile cathProfile $
|
||||
\alice bob cath -> do
|
||||
connectUsers alice bob
|
||||
alice ##> "/ad"
|
||||
(shortLink, _) <- getContactLinks alice True
|
||||
alice ##> "/p bob"
|
||||
alice <## "user profile is changed to bob (your 1 contacts are notified)"
|
||||
bob <## "contact alice changed to bob_1"
|
||||
bob <## "use @bob_1 <message> to send messages"
|
||||
alice ##> "/p"
|
||||
alice <## "user profile: bob"
|
||||
alice <## "use /p <name> [<bio>] to change it"
|
||||
alice ##> "/users"
|
||||
alice <## "bob_1 (active)"
|
||||
alice #> "@bob hi"
|
||||
bob <# "bob_1> hi"
|
||||
cath ##> ("/_connect plan 1 " <> shortLink)
|
||||
cath <## "contact address: ok to connect"
|
||||
sLinkData <- getTermLine cath
|
||||
sLinkData `shouldContain` "\"displayName\":\"bob\""
|
||||
alice ##> "/p alice"
|
||||
alice <## "user profile is changed to alice (your 1 contacts are notified)"
|
||||
bob <## "contact bob_1 changed to alice"
|
||||
bob <## "use @alice <message> to send messages"
|
||||
alice ##> "/users"
|
||||
alice <## "alice (active)"
|
||||
|
||||
testUpdateProfileNameUsedByHiddenUser :: HasCallStack => TestParams -> IO ()
|
||||
testUpdateProfileNameUsedByHiddenUser =
|
||||
testChat aliceProfile $
|
||||
\alice -> do
|
||||
alice ##> "/create user alisa"
|
||||
showActiveUser alice "alisa"
|
||||
alice ##> "/hide user my_password"
|
||||
alice <## "current user alisa:"
|
||||
alice <## "messages are hidden (use /tail to view)"
|
||||
alice <## "profile is hidden"
|
||||
alice ##> "/user alice"
|
||||
showActiveUser alice "alice (Alice)"
|
||||
alice ##> "/p alisa"
|
||||
alice <## "invalid display name: alisa"
|
||||
|
||||
testUpdateProfileNameUsedByUser :: HasCallStack => TestParams -> IO ()
|
||||
testUpdateProfileNameUsedByUser =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
connectUsers alice bob
|
||||
alice ##> "/create user alisa"
|
||||
showActiveUser alice "alisa"
|
||||
alice ##> "/user alice"
|
||||
showActiveUser alice "alice (Alice)"
|
||||
alice ##> "/p alisa"
|
||||
alice <## "user with the name alisa already exists"
|
||||
alice ##> "/p alisa_1"
|
||||
alice <## "invalid display name: alisa_1"
|
||||
alice <## "you could use this one: alisa-1"
|
||||
alice ##> "/p alisa-1"
|
||||
alice <## "user profile is changed to alisa-1 (your 1 contacts are notified)"
|
||||
bob <## "contact alice changed to alisa-1"
|
||||
bob <## "use @alisa-1 <message> to send messages"
|
||||
alice ##> "/create user alisa-1"
|
||||
alice <## "user with the name alisa-1 already exists"
|
||||
alice ##> "/create user bob_2"
|
||||
alice <## "invalid display name: bob_2"
|
||||
alice <## "you could use this one: bob-2"
|
||||
alice ##> "/users"
|
||||
alice <## "alisa"
|
||||
alice <## "alisa-1 (active)"
|
||||
|
||||
-- Profile.description survives the connect-time round-trip and is shown in the contact /i view.
|
||||
testProfileDescriptionShown :: HasCallStack => TestParams -> IO ()
|
||||
testProfileDescriptionShown =
|
||||
|
||||
@@ -33,6 +33,7 @@ import Simplex.Chat.Protocol
|
||||
GrpMsgForward (GrpMsgForward),
|
||||
MsgContent (MCText),
|
||||
VerifiedMsg (VMUnsigned),
|
||||
fwdMemberName,
|
||||
maxBatchElementCount,
|
||||
maxEncodedMsgLength,
|
||||
mcSimple,
|
||||
@@ -49,6 +50,7 @@ batchingTests = describe "message batching tests" $ do
|
||||
it "splits a batch that exceeds the element count limit" testBatchElementCountLimit
|
||||
it "does not create a relay delivery body when every task is oversized" testRelayBatchAllLarge
|
||||
it "classifies a task that fits raw but not as a framed singleton as large" testRelayBatchSingletonOverflow
|
||||
it "shortens forwarded member names" testFwdMemberName
|
||||
|
||||
instance IsString SndMessage where
|
||||
fromString s = SndMessage {msgId, sharedMsgId = SharedMsgId "", msgBody = s', signedMsg_ = Nothing}
|
||||
@@ -190,6 +192,11 @@ testRelayBatchSingletonOverflow = do
|
||||
map deliveryTaskId accepted `shouldBe` []
|
||||
map deliveryTaskId large `shouldBe` [1]
|
||||
|
||||
testFwdMemberName :: IO ()
|
||||
testFwdMemberName = do
|
||||
fwdMemberName "sixteen_chars_ab" `shouldBe` "sixteen_chars_ab"
|
||||
fwdMemberName "seventeen_chars_a" `shouldBe` "seventeen_chars_…"
|
||||
|
||||
runBatcherTest :: BatchMode -> Int -> [SndMessage] -> [ChatError] -> [ByteString] -> Spec
|
||||
runBatcherTest mode maxLen msgs expectedErrors expectedBatches =
|
||||
it
|
||||
|
||||
@@ -369,17 +369,11 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberRestrictions\":{\"restriction\":\"blocked\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile, memberKey = Nothing} (Just MemberRestrictions {restriction = MRSBlocked})
|
||||
it "x.grp.mem.inv" $
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"memberIntro\":{\"directConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
||||
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq}
|
||||
it "x.grp.mem.inv w/t directConnReq" $
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.inv\",\"params\":{\"memberId\":\"AQIDBA==\",\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"}}}"
|
||||
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
||||
it "x.grp.mem.fwd" $
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"directConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile, memberKey = Nothing} IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq}
|
||||
it "x.grp.mem.fwd with member chat version range and w/t directConnReq" $
|
||||
#==# XGrpMemInv (MemberId "\1\2\3\4") IntroInvitation {groupConnReq = testConnReq}
|
||||
it "x.grp.mem.fwd with member chat version range" $
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-4%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"9-21\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}"
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile, memberKey = Nothing} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing}
|
||||
#==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile, memberKey = Nothing} IntroInvitation {groupConnReq = testConnReq}
|
||||
it "x.grp.mem.info" $
|
||||
"{\"v\":\"9\",\"event\":\"x.grp.mem.info\",\"params\":{\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
|
||||
#==# XGrpMemInfo (MemberId "\1\2\3\4") testProfile
|
||||
|
||||
@@ -57,3 +57,10 @@ testMkValidName = do
|
||||
mkValidName "123 alice" `shouldBe` "123 alice"
|
||||
mkValidName "01234567890123456789012345678901234567890123456789extra" `shouldBe` "01234567890123456789012345678901234567890123456789"
|
||||
mkValidName "0123456789012345678901234567890123456789012345678 extra" `shouldBe` "0123456789012345678901234567890123456789012345678"
|
||||
mkValidName "alice_1" `shouldBe` "alice-1"
|
||||
mkValidName "alice_bob_12" `shouldBe` "alice_bob-12"
|
||||
mkValidName "alice _1" `shouldBe` "alice -1"
|
||||
mkValidName "alice_" `shouldBe` "alice_"
|
||||
mkValidName "alice_bob" `shouldBe` "alice_bob"
|
||||
mkValidName "alice_1b" `shouldBe` "alice_1b"
|
||||
mkValidName (mkValidName "alice_1") `shouldBe` "alice-1"
|
||||
Reference in new issue
Block a user