langchain-hs-0.0.5.0: test/Test/Langchain/Retriever/BM25Spec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.Langchain.Retriever.BM25Spec (tests) where
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Langchain.DocumentLoader.Core (Document (..))
import Langchain.Retriever.BM25
tests :: TestTree
tests =
testGroup
"Langchain.Retriever.BM25"
[ testCase "BM25 finds exact matching document" $ do
let doc1 =
Document
{ pageContent = "Haskell is a functional programming language with strong static types."
, metadata = Map.empty
}
doc2 =
Document
{ pageContent = "Python is a dynamic language used for machine learning and web scripts."
, metadata = Map.empty
}
doc3 =
Document
{ pageContent = "Rust guarantees memory safety without garbage collection."
, metadata = Map.empty
}
index = newBM25Index [doc1, doc2, doc3]
results = bm25Search index "functional static types" 2
case results of
(topResult : _) -> topResult @?= doc1
[] -> assertFailure "Expected non-empty search results"
, testCase "BM25 scoring gives highest score to relevant passage" $ do
let doc1 =
Document
{ pageContent = "Deep research agent explores web pages and validates claims."
, metadata = Map.empty
}
doc2 =
Document
{ pageContent = "Database query optimization and index scans in postgresql."
, metadata = Map.empty
}
index = newBM25Index [doc1, doc2]
scored = bm25SearchWithScores index "deep research agent" 2
case scored of
[(bestDoc, score)] -> do
bestDoc @?= doc1
assertBool "Score should be positive" (score > 0.0)
_ -> assertFailure ("Expected 1 scored result, got " ++ show (length scored))
, testCase "addDocumentsBM25 updates index correctly" $ do
let doc1 = Document {pageContent = "Alpha beta gamma", metadata = Map.empty}
doc2 = Document {pageContent = "Delta epsilon zeta", metadata = Map.empty}
index1 = newBM25Index [doc1]
index2 = addDocumentsBM25 [doc2] index1
results = bm25Search index2 "epsilon" 5
results @?= [doc2]
, testProperty "Tokenize lowercases and strips punctuation" $
\s ->
let txt = T.pack s
tokens = tokenize txt
in all (\t -> T.toLower t == t) tokens
]