packages feed

espial-0.0.40: test/Model/NotesBulkEditSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Model.NotesBulkEditSpec (spec) where

import Database.Persist.Sql (fromSqlKey)
import Model.Custom (hashPasswordBCryptWithPolicy, mkNtSlug)
import TestImport
import Types (DB)

t0 :: UTCTime
t0 = UTCTime (fromGregorian 2024 1 1) 0

createTestUser :: DB (Key User)
createTestUser = do
  pwHash <- liftIO $ hashPasswordBCryptWithPolicy bcryptTestPolicy "pass"
  insert $ User "testuser" pwHash Nothing False False True True False False True Nothing

createNote :: Key User -> Text -> Bool -> DB (Key Note)
createNote uid title = createNoteIsMarkdown uid title False

createNoteIsMarkdown :: Key User -> Text -> Bool -> Bool -> DB (Key Note)
createNoteIsMarkdown uid title isMarkdown shared = do
  slug <- liftIO mkNtSlug
  insert $ Note uid slug (length title) title "text" isMarkdown shared t0 t0

getNoteShared :: Key Note -> DB (Maybe Bool)
getNoteShared nid = fmap (fmap (noteShared . entityVal)) (selectFirst [NoteId ==. nid] [])

getNoteIsMarkdown :: Key Note -> DB (Maybe Bool)
getNoteIsMarkdown nid = fmap (fmap (noteIsMarkdown . entityVal)) (selectFirst [NoteId ==. nid] [])

getNoteExists :: Key Note -> DB Bool
getNoteExists nid = fmap isJust (selectFirst [NoteId ==. nid] [])

mkNoteBulkEditForm :: Int -> Maybe Text -> NoteBulkAction -> [Key Note] -> NoteBulkEditForm
mkNoteBulkEditForm cnt mquery action nids =
  NoteBulkEditForm
    { _nbeSelection =
        if null nids
          then NoteBulkSelectionAll SharedAll mquery
          else NoteBulkSelectionPage (map fromSqlKey nids),
      _nbeAction = action,
      _nbeSelectionCount = cnt
    }

spec :: Spec
spec = withApp $ do
  describe "notesBulkEdit" $ do
    describe "NoteBulkSelectionPage" $ do
      it "makes selected notes private" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" True
          nid2 <- createNote uid "b" True
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 2 Nothing NoteBulkActionPrivate [nid1, nid2]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 2
        shared1 <- runDB $ getNoteShared nid1
        shared2 <- runDB $ getNoteShared nid2
        liftIO $ shared1 `shouldBe` Just False
        liftIO $ shared2 `shouldBe` Just False

      it "makes selected notes public" $ do
        (uid, nid1) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" False
          return (uid, nid1)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionPublic [nid1]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        shared1 <- runDB $ getNoteShared nid1
        liftIO $ shared1 `shouldBe` Just True

      it "sets selected notes to use markdown" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNoteIsMarkdown uid "a" False False
          nid2 <- createNoteIsMarkdown uid "b" False False
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionMarkdown [nid1]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        md1 <- runDB $ getNoteIsMarkdown nid1
        md2 <- runDB $ getNoteIsMarkdown nid2
        liftIO $ md1 `shouldBe` Just True
        liftIO $ md2 `shouldBe` Just False

      it "sets selected notes to use plaintext" $ do
        (uid, nid1) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNoteIsMarkdown uid "a" True False
          return (uid, nid1)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionPlaintext [nid1]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        md1 <- runDB $ getNoteIsMarkdown nid1
        liftIO $ md1 `shouldBe` Just False

      it "deletes only the selected notes" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" False
          nid2 <- createNote uid "b" False
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionDelete [nid1]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        exists1 <- runDB $ getNoteExists nid1
        exists2 <- runDB $ getNoteExists nid2
        liftIO $ exists1 `shouldBe` False
        liftIO $ exists2 `shouldBe` True

      it "does not modify notes of another user" $ do
        (uid1, nid2) <- runDB $ do
          uid1 <- createTestUser
          pwHash <- liftIO $ hashPasswordBCryptWithPolicy bcryptTestPolicy "pass"
          uid2 <- insert $ User "otheruser" pwHash Nothing False False True True False False True Nothing
          nid2 <- createNote uid2 "other" True
          return (uid1, nid2)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionPrivate [nid2]
        _ <- runDB $ notesBulkEdit uid1 form
        shared2 <- runDB $ getNoteShared nid2
        liftIO $ shared2 `shouldBe` Just True

      it "page count mismatch returns Left BulkEditErrorPageMismatch" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" False
          nid2 <- createNote uid "b" False
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 1 Nothing NoteBulkActionDelete [nid1, nid2]
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Left BulkEditErrorPageMismatch

    describe "NoteBulkSelectionAll" $ do
      it "applies the action to all notes matching the query" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "groceries" True
          nid2 <- createNote uid "work" True
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 1 (Just "groceries") NoteBulkActionPrivate []
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        shared1 <- runDB $ getNoteShared nid1
        shared2 <- runDB $ getNoteShared nid2
        liftIO $ shared1 `shouldBe` Just False
        liftIO $ shared2 `shouldBe` Just True

      it "applies the action only to notes matching the shared filter" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" True
          nid2 <- createNote uid "b" False
          return (uid, nid1, nid2)
        let form = NoteBulkEditForm (NoteBulkSelectionAll SharedPublic Nothing) NoteBulkActionMarkdown 1
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 1
        md1 <- runDB $ getNoteIsMarkdown nid1
        md2 <- runDB $ getNoteIsMarkdown nid2
        liftIO $ md1 `shouldBe` Just True
        liftIO $ md2 `shouldBe` Just False

      it "deletes all notes when no query is given" $ do
        (uid, nid1, nid2) <- runDB $ do
          uid <- createTestUser
          nid1 <- createNote uid "a" False
          nid2 <- createNote uid "b" True
          return (uid, nid1, nid2)
        let form = mkNoteBulkEditForm 2 Nothing NoteBulkActionDelete []
        result <- runDB $ notesBulkEdit uid form
        liftIO $ result `shouldBe` Right 2
        exists1 <- runDB $ getNoteExists nid1
        exists2 <- runDB $ getNoteExists nid2
        liftIO $ exists1 `shouldBe` False
        liftIO $ exists2 `shouldBe` False