packages feed

espial-0.0.41: test/Model/NotesSearchSpec.hs

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

module Model.NotesSearchSpec (spec) where

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 -> Bool -> DB (Key Note)
createNote uid title isMarkdown shared = do
  slug <- liftIO mkNtSlug
  insert $ Note uid slug (length title) title "text" isMarkdown shared t0 t0

search :: Key User -> SharedP -> Maybe Text -> DB [Key Note]
search uid sharedp mquery = do
  (_, rows, _, _) <- getNoteList uid mquery sharedp (mkNotePaging defaultNoteSort Nothing 1) 100
  return $ map entityKey rows

spec :: Spec
spec = withApp $ do
  describe "getNoteList" $ do
    it "markdown:true matches only markdown notes" $ do
      (uid, nid1, nid2) <- runDB $ do
        uid <- createTestUser
        nid1 <- createNote uid "a" True False
        nid2 <- createNote uid "b" False False
        return (uid, nid1, nid2)
      nids <- runDB $ search uid SharedAll (Just "markdown:true")
      liftIO $ nids `shouldBe` [nid1]
      nids' <- runDB $ search uid SharedAll (Just "m:false")
      liftIO $ nids' `shouldBe` [nid2]

    it "falls back to all-field search for a non-boolean markdown value" $ do
      (uid, nid1) <- runDB $ do
        uid <- createTestUser
        nid1 <- createNote uid "markdown:tutorial" False False
        _ <- createNote uid "other" True False
        return (uid, nid1)
      nids <- runDB $ search uid SharedAll (Just "markdown:tutorial")
      liftIO $ nids `shouldBe` [nid1]

    it "title= matches only when the entire title equals the term" $ do
      (uid, nExact) <- runDB $ do
        uid <- createTestUser
        nExact <- createNote uid "haskell" False False
        _ <- createNote uid "haskell tutorial" False False
        return (uid, nExact)
      nids <- runDB $ search uid SharedAll (Just "title=haskell")
      liftIO $ nids `shouldBe` [nExact]

    it "d= matches only when the entire text equals the term" $ do
      (uid, nid) <- runDB $ do
        uid <- createTestUser
        nid <- createNote uid "a" False False
        return (uid, nid)
      nids <- runDB $ search uid SharedAll (Just "d=text")
      liftIO $ nids `shouldBe` [nid]
      nids' <- runDB $ search uid SharedAll (Just "d=tex")
      liftIO $ nids' `shouldBe` []

    it "supports parentheses grouping with OR and NOT" $ do
      (uid, nApple, nBananaCherry, nBanana) <- runDB $ do
        uid <- createTestUser
        nApple <- createNote uid "apple pie" False False
        nBananaCherry <- createNote uid "banana cherry smoothie" False False
        nBanana <- createNote uid "banana bread" False False
        return (uid, nApple, nBananaCherry, nBanana)
      nids <- runDB $ search uid SharedAll (Just "apple|(banana cherry)")
      liftIO $ sort nids `shouldBe` sort [nApple, nBananaCherry]
      nids' <- runDB $ search uid SharedAll (Just "-(banana cherry)")
      liftIO $ sort nids' `shouldBe` sort [nApple, nBanana]

    it "the shared filter restricts results" $ do
      (uid, nid1, nid2) <- runDB $ do
        uid <- createTestUser
        nid1 <- createNote uid "a" False True
        nid2 <- createNote uid "b" False False
        return (uid, nid1, nid2)
      publicNids <- runDB $ search uid SharedPublic Nothing
      liftIO $ publicNids `shouldBe` [nid1]
      privateNids <- runDB $ search uid SharedPrivate Nothing
      liftIO $ privateNids `shouldBe` [nid2]
      allNids <- runDB $ search uid SharedAll Nothing
      liftIO $ sort allNids `shouldBe` sort [nid1, nid2]