Files
simplex-chat/tests/ChatTests/Local.hs
Diogo 4d82209a3a core: pagination API to load items around defined or the earliest unread item (#5100)
* core: auto increment chat item ids (#5088)

* core: auto increment chat item ids

* file name

* down name

* update schema

* ignore down migration on schema dump test

* fix testDirectMessageDelete test

* fix testNotes test

* core: initial api support for items around a given item (#5092)

* core: initial api support for items around a given item

* implementation and tests for local messages

* pass entities down

* unused

* getAllChatItems implementation and tests

* pagination for getting chat and tests

* remove unused import

* group implementation and tests

* refactor

* order by created at for local and direct chats

* core: initial landing api for chat and gaps  (#5104)

* initial work on initial param for loading chat

* support for initial

* controller parse

* fixed sqls

* refactor names

* fix ChatLandingSection serialized type

* total accuracy on landing section

* descriptive view message

* foldr

* refactor to make landingSection reusable

* refactor: use foldr everywhere

* propagate search

* Revert "propagate search"

This reverts commit 01611fd719.

* throw when search is sent for initial

* gap size wip (needs testing)

* final

* remove order by

* remove index

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* core: fix initial api latest chat items ordering (#5151)

* core: fix one item missing from latest in initial and wrong check (#5153)

* core: fix one item missing from latest in initial and wrong check

* final fixes and tests

* clearer tests

* core: remove gaps and make sure page size is always the same (#5163)

* remove gaps

* consistent pagination size

* proper fix and around fix too

* optimize

* refactor

* core: simplify pagination

* core: first unread queries (#5174)

* core: pagination nav info (#5175)

* core: pagination nav info

* wip

* rework

* rework

* group, local

* fix

* rename

* fix tests

* just

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-14 08:34:25 +00:00

231 lines
9.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 hiding (it)
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
describe "batch create messages" $ do
it "create multiple messages api" testCreateMulti
it "create multiple messages with files" testCreateMultiFiles
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 2 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 around=2 count=1", chat, [(1, "hello world"), (1, "memento mori"), (1, "knock-knock")])
alice #$> ("/_get chat *1 around=2 count=3", chat, [(1, "hello world"), (1, "memento mori"), (1, "knock-knock"), (1, "who's there?")])
alice #$> ("/_get chat *1 around=3 count=10", chat, [(1, "hello world"), (1, "memento mori"), (1, "knock-knock"), (1, "who's there?")])
alice #$> ("/_get chat *1 around=4 count=1", chat, [(1, "knock-knock"), (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 <## "notes: all messages are removed"
alice ##> "/fs 1"
alice <## "file 1 not found"
alice ##> "/tail"
doesFileExist stored `shouldReturn` False
testOtherFiles :: FilePath -> IO ()
testOtherFiles =
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> withXFTPServer $ do
connectUsers alice bob
createCCNoteFolder bob
bob ##> "/_files_folder ./tests/tmp/"
bob <## "ok"
alice #> "/f @bob ./tests/fixtures/test.jpg"
alice <## "use /fc 1 to cancel sending"
bob <# "alice> sends file test.jpg (136.5 KiB / 139737 bytes)"
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
alice <## "completed uploading file 1 (test.jpg) for bob"
bob ##> "/fr 1"
bob
<### [ "saving file 1 from alice to test.jpg",
"started receiving file 1 (test.jpg) from alice"
]
bob <## "completed receiving file 1 (test.jpg) from alice"
bob /* "test"
bob ##> "/tail *"
bob <# "* test"
bob ##> "/clear *"
bob <## "notes: all messages are removed"
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}}
testCreateMulti :: FilePath -> IO ()
testCreateMulti tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
createCCNoteFolder alice
alice ##> "/_create *1 json [{\"msgContent\": {\"type\": \"text\", \"text\": \"test 1\"}}, {\"msgContent\": {\"type\": \"text\", \"text\": \"test 2\"}}]"
alice <# "* test 1"
alice <# "* test 2"
testCreateMultiFiles :: FilePath -> IO ()
testCreateMultiFiles tmp = withNewTestChat tmp "alice" aliceProfile $ \alice -> do
createCCNoteFolder alice
alice #$> ("/_files_folder ./tests/tmp/alice_app_files", id, "ok")
copyFile "./tests/fixtures/test.jpg" "./tests/tmp/alice_app_files/test.jpg"
copyFile "./tests/fixtures/test.pdf" "./tests/tmp/alice_app_files/test.pdf"
let cm1 = "{\"msgContent\": {\"type\": \"text\", \"text\": \"message without file\"}}"
cm2 = "{\"filePath\": \"test.jpg\", \"msgContent\": {\"type\": \"text\", \"text\": \"sending file 1\"}}"
cm3 = "{\"filePath\": \"test.pdf\", \"msgContent\": {\"type\": \"text\", \"text\": \"sending file 2\"}}"
alice ##> ("/_create *1 json [" <> cm1 <> "," <> cm2 <> "," <> cm3 <> "]")
alice <# "* message without file"
alice <# "* sending file 1"
alice <# "* file 1 (test.jpg)"
alice <# "* sending file 2"
alice <# "* file 2 (test.pdf)"
doesFileExist "./tests/tmp/alice_app_files/test.jpg" `shouldReturn` True
doesFileExist "./tests/tmp/alice_app_files/test.pdf" `shouldReturn` True
alice ##> "/_get chat *1 count=3"
r <- chatF <$> getTermLine alice
r `shouldBe` [((1, "message without file"), Nothing), ((1, "sending file 1"), Just "test.jpg"), ((1, "sending file 2"), Just "test.pdf")]