mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-01 07:26:03 +00:00
* Add chat type "self"
* rename to Notes
* cover more things
* remove quote, tweak sql
* resolve comments
* constrain ACIQDirection to exclude CTLocal
* add CILocalRcv handling
* plug in migrations and tests
* cover more API, implement new folders
* working create/send/tail
* remove interaction with messages
* add note deletion (api-only)
* add folder deletion
* add getLocalChatItemIdByText
* add APICreateChatItem and files
* add protocol check for getFileTransfer protocol
* replace FTLocal with createLocalFile
* add chat previews
* add folder clear
* add reactions
* add read/unread
* add note updates
* resolve some comments
* remove local reactions
* remove folder names, deletion, add autocreate
* add file deletion check
* add preview pagination test
* add per-item file deletion check
* pull mkChatItem out of createLocal to prevent ci record updates
* use - as notes name
* bump migration ts
* update schema
* resolve comments
* add chat pagination test
* use chat queries from Direct instead
* evict note folders from createUserRecord
* switch to - for note folder chat type prefix and use empty name
* fix getLocalChatXxx
* add explicit createCCNoteFolder for tests
* use overloadedstrings for single-line queries
* add suggested chat list tests
* add notes chat to a user-creating test
* throw correct error for missing file
* remove unique check from schema
* add UndecidableInstances for ghc8.10
* switch to * for chat type sigil
* add file safety test
* add drop index
* remove indentation
* remove repeated folder
* remove redundant filter query, NoteFolderName
* don't attempt to cancel local files when deleting chat item
* rename function
* fix comment
* rename
* fix merge
* fix typo
* remove editable limit
* restore comment
* remove local file cancel
* Revert "remove editable limit"
This reverts commit 65df55caf8.
* refactor
---------
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
190 lines
7.0 KiB
Haskell
190 lines
7.0 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE PostfixOperators #-}
|
|
|
|
module ChatTests.Local where
|
|
|
|
import ChatClient
|
|
import ChatTests.ChatList (getChats_)
|
|
import ChatTests.Utils
|
|
import Data.Time (getCurrentTime)
|
|
import Data.Time.Format.ISO8601 (iso8601Show)
|
|
import Simplex.Chat.Controller (ChatConfig (..), InlineFilesConfig (..), defaultInlineFilesConfig)
|
|
import System.Directory (copyFile, doesFileExist)
|
|
import System.FilePath ((</>))
|
|
import Test.Hspec
|
|
import UnliftIO.Async (concurrently_)
|
|
|
|
chatLocalChatsTests :: SpecWith FilePath
|
|
chatLocalChatsTests = do
|
|
describe "note folders" $ do
|
|
it "create folders, add notes, read, search" testNotes
|
|
it "switch users" testUserNotes
|
|
it "preview pagination for notes" testPreviewsPagination
|
|
it "chat pagination" testChatPagination
|
|
it "stores files" testFiles
|
|
it "deleting files does not interfere with other chat types" testOtherFiles
|
|
|
|
testNotes :: FilePath -> IO ()
|
|
testNotes tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
createCCNoteFolder alice
|
|
|
|
alice ##> "/contacts"
|
|
-- not a contact
|
|
|
|
alice /* "keep in mind"
|
|
alice ##> "/tail"
|
|
alice <# "* keep in mind"
|
|
alice ##> "/chats"
|
|
alice <# "* keep in mind"
|
|
alice ##> "/? keep"
|
|
alice <# "* keep in mind"
|
|
|
|
alice #$> ("/_read chat *1 from=1 to=100", id, "ok")
|
|
alice ##> "/_unread chat *1 on"
|
|
alice <## "ok"
|
|
|
|
alice ##> "/_delete item *1 1 internal"
|
|
alice <## "message deleted"
|
|
alice ##> "/tail"
|
|
alice ##> "/chats"
|
|
|
|
alice /* "ahoy!"
|
|
alice ##> "/_update item *1 1 text Greetings."
|
|
alice ##> "/tail *"
|
|
alice <# "* Greetings."
|
|
|
|
testUserNotes :: FilePath -> IO ()
|
|
testUserNotes tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
createCCNoteFolder alice
|
|
|
|
alice /* "keep in mind"
|
|
alice ##> "/tail"
|
|
alice <# "* keep in mind"
|
|
|
|
alice ##> "/create user secret"
|
|
alice <## "user profile: secret"
|
|
alice <## "use /p <display name> to change it"
|
|
alice <## "(the updated profile will be sent to all your contacts)"
|
|
|
|
alice ##> "/tail"
|
|
|
|
alice ##> "/_delete item *1 1 internal"
|
|
alice <## "chat db error: SENoteFolderNotFound {noteFolderId = 1}"
|
|
|
|
testPreviewsPagination :: FilePath -> IO ()
|
|
testPreviewsPagination tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
createCCNoteFolder alice
|
|
|
|
tsS <- iso8601Show <$> getCurrentTime
|
|
alice /* "first"
|
|
tsM <- iso8601Show <$> getCurrentTime
|
|
alice /* "last"
|
|
tsE <- iso8601Show <$> getCurrentTime
|
|
|
|
-- there's only one folder that got updated after tsM and before tsE
|
|
getChats_ alice "count=3" [("*", "last")]
|
|
getChats_ alice ("after=" <> tsE <> " count=10") []
|
|
getChats_ alice ("after=" <> tsS <> " count=10") [("*", "last")]
|
|
getChats_ alice ("before=" <> tsM <> " count=10") []
|
|
getChats_ alice ("before=" <> tsE <> " count=10") [("*", "last")]
|
|
getChats_ alice ("before=" <> tsS <> " count=10") []
|
|
|
|
testChatPagination :: FilePath -> IO ()
|
|
testChatPagination tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
createCCNoteFolder alice
|
|
|
|
alice /* "hello world"
|
|
alice /* "memento mori"
|
|
alice /* "knock-knock"
|
|
alice /* "who's there?"
|
|
|
|
alice #$> ("/_get chat *1 count=100", chat, [(1, "hello world"), (1, "memento mori"), (1, "knock-knock"), (1, "who's there?")])
|
|
alice #$> ("/_get chat *1 count=1", chat, [(1, "who's there?")])
|
|
alice #$> ("/_get chat *1 after=2 count=10", chat, [(1, "knock-knock"), (1, "who's there?")])
|
|
alice #$> ("/_get chat *1 after=2 count=2", chat, [(1, "knock-knock"), (1, "who's there?")])
|
|
alice #$> ("/_get chat *1 after=1 count=2", chat, [(1, "memento mori"), (1, "knock-knock")])
|
|
alice #$> ("/_get chat *1 before=3 count=10", chat, [(1, "hello world"), (1, "memento mori")])
|
|
alice #$> ("/_get chat *1 before=3 count=2", chat, [(1, "hello world"), (1, "memento mori")])
|
|
alice #$> ("/_get chat *1 before=4 count=2", chat, [(1, "memento mori"), (1, "knock-knock")])
|
|
|
|
alice #$> ("/_get chat *1 count=10 search=k-k", chat, [(1, "knock-knock")])
|
|
|
|
testFiles :: FilePath -> IO ()
|
|
testFiles tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
-- setup
|
|
createCCNoteFolder alice
|
|
let files = "./tests/tmp/app_files"
|
|
alice ##> ("/_files_folder " <> files)
|
|
alice <## "ok"
|
|
|
|
-- ui-like upload
|
|
let source = "./tests/fixtures/test.jpg"
|
|
let stored = files </> "test.jpg"
|
|
copyFile source stored
|
|
alice ##> "/_create *1 json {\"filePath\": \"test.jpg\", \"msgContent\": {\"text\":\"hi myself\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}"
|
|
alice <# "* hi myself"
|
|
alice <# "* file 1 (test.jpg)"
|
|
|
|
alice ##> "/tail"
|
|
alice <# "* hi myself"
|
|
alice <# "* file 1 (test.jpg)"
|
|
|
|
alice ##> "/_get chat *1 count=100"
|
|
r <- chatF <$> getTermLine alice
|
|
r `shouldBe` [((1, "hi myself"), Just "test.jpg")]
|
|
|
|
alice ##> "/fs 1"
|
|
alice <## "bad chat command: not supported for local files"
|
|
|
|
alice ##> "/fc 1"
|
|
alice <## "chat db error: SELocalFileNoTransfer {fileId = 1}"
|
|
|
|
-- one more file
|
|
let stored2 = files </> "another_test.jpg"
|
|
copyFile source stored2
|
|
alice ##> "/_create *1 json {\"filePath\": \"another_test.jpg\", \"msgContent\": {\"text\":\"\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}"
|
|
alice <# "* file 2 (another_test.jpg)"
|
|
|
|
alice ##> "/_delete item *1 2 internal"
|
|
alice <## "message deleted"
|
|
doesFileExist stored2 `shouldReturn` False
|
|
doesFileExist stored `shouldReturn` True
|
|
|
|
alice ##> "/clear *"
|
|
alice ##> "/fs 1"
|
|
alice <## "chat db error: SEChatItemNotFoundByFileId {fileId = 1}"
|
|
alice ##> "/tail"
|
|
doesFileExist stored `shouldReturn` False
|
|
|
|
testOtherFiles :: FilePath -> IO ()
|
|
testOtherFiles =
|
|
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do
|
|
connectUsers alice bob
|
|
createCCNoteFolder bob
|
|
bob ##> "/_files_folder ./tests/tmp/"
|
|
bob <## "ok"
|
|
alice ##> "/_send @2 json {\"msgContent\":{\"type\":\"voice\", \"duration\":10, \"text\":\"\"}, \"filePath\":\"./tests/fixtures/test.jpg\"}"
|
|
alice <# "@bob voice message (00:10)"
|
|
alice <# "/f @bob ./tests/fixtures/test.jpg"
|
|
-- below is not shown in "sent" mode
|
|
-- alice <## "use /fc 1 to cancel sending"
|
|
bob <# "alice> voice message (00:10)"
|
|
bob <# "alice> sends file test.jpg (136.5 KiB / 139737 bytes)"
|
|
-- below is not shown in "sent" mode
|
|
-- bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
bob <## "started receiving file 1 (test.jpg) from alice"
|
|
concurrently_
|
|
(alice <## "completed sending file 1 (test.jpg) to bob")
|
|
(bob <## "completed receiving file 1 (test.jpg) from alice")
|
|
|
|
bob /* "test"
|
|
bob ##> "/tail *"
|
|
bob <# "* test"
|
|
bob ##> "/clear *"
|
|
bob ##> "/tail *"
|
|
bob ##> "/fs 1"
|
|
bob <## "receiving file 1 (test.jpg) complete, path: test.jpg"
|
|
doesFileExist "./tests/tmp/test.jpg" `shouldReturn` True
|
|
where
|
|
cfg = testCfg {inlineFiles = defaultInlineFilesConfig {offerChunks = 100, sendChunks = 100, receiveChunks = 100}}
|