1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE ScopedTypeVariables #-}
3 {-# LANGUAGE TypeApplications #-}
4 module Core.Text.Corpus.Query (tests) where
7 import Gargantext.Core.Text.Corpus.Query
8 import Gargantext.Core.Types
10 import qualified Gargantext.Core.Text.Corpus.API.Arxiv as Arxiv
11 import qualified Network.Api.Arxiv as Arxiv
12 import qualified Gargantext.Core.Text.Corpus.API.Pubmed as Pubmed
15 import Test.Tasty.HUnit
16 import Test.Tasty.QuickCheck hiding (Positive, Negative)
19 tests = testGroup "Boolean Query Engine" [
20 testProperty "Parses 'A OR B'" testParse01
21 , testProperty "Parses 'A AND B'" testParse02
22 , testProperty "Parses '-A'" testParse03
23 , testProperty "Parses 'NOT A'" testParse03_01
24 , testProperty "Parses 'A -B'" testParse04
25 , testProperty "Parses 'A NOT -B'" testParse04_01
26 , testProperty "Parses 'A AND B -C' (left associative)" testParse05
27 , testProperty "Parses 'A AND (B -C)' (right associative)" testParse05_01
28 , testProperty "Parses (A OR B OR NOT C) AND (D OR E OR F) -(G OR H OR I)" testParse06
29 , testCase "Parses words into a single constant" testWordsIntoConst
30 , testGroup "Arxiv expression converter" [
31 testCase "It supports 'A AND B'" testArxiv01_01
32 , testCase "It supports '\"Haskell\" AND \"Agda\"'" testArxiv01_02
33 , testCase "It supports 'A OR B'" testArxiv02
34 , testCase "It supports 'A AND NOT B'" testArxiv03_01
35 , testCase "It supports 'A AND -B'" testArxiv03_02
36 , testCase "It supports 'A AND -B'" testArxiv03_02
37 , testCase "It supports 'A AND NOT (NOT B)'" testArxiv04_01
38 , testCase "It supports 'A AND NOT (NOT (NOT B))'" testArxiv04_02
39 , testCase "It supports 'A OR NOT B'" testArxiv05
40 , testCase "It supports '-A'" testArxiv06
42 , testGroup "PUBMED expression converter" [
43 testCase "It supports 'A'" testPubMed01
44 , testCase "It supports '-A'" testPubMed02_01
45 , testCase "It supports 'NOT A'" testPubMed02_02
46 , testCase "It supports 'NOT (NOT A)'" testPubMed02_03
47 , testCase "It supports '\"Haskell\" AND \"Idris\"'" testPubMed03
48 , testCase "It supports 'A OR B'" testPubMed04
52 -- | Checks that the 'RawQuery' can be translated into the expected 'BoolExpr' form,
53 -- by also checking that both renders back to the initial 'RawQuery'.
54 translatesInto :: RawQuery -> BoolExpr Term -> Property
55 (translatesInto) raw boolExpr =
56 let parsed = parseQuery raw
57 expected = Right (unsafeMkQuery boolExpr)
58 in counterexample (show parsed <> " != " <> show expected) $
59 (renderQuery <$> parsed) === (renderQuery <$> expected)
61 testParse01 :: Property
62 testParse01 = "A OR B" `translatesInto` (BConst (Positive "A") `BOr` BConst (Positive "B"))
64 testParse02 :: Property
65 testParse02 = "A AND B" `translatesInto` (BConst (Positive "A") `BAnd` BConst (Positive "B"))
67 testParse03 :: Property
68 testParse03 = "-A" `translatesInto` (BConst (Negative "A"))
70 testParse03_01 :: Property
71 testParse03_01 = "NOT A" `translatesInto` (BConst (Negative "A"))
73 testParse04 :: Property
74 testParse04 = "A -B" `translatesInto` (BConst (Positive "A") `BAnd` BConst (Negative "B"))
76 -- Both 'A -B' and 'A AND -B' desugars into the same form.
77 testParse04_01 :: Property
78 testParse04_01 = "A AND -B" `translatesInto` (BConst (Positive "A") `BAnd` BConst (Negative "B"))
80 testParse05 :: Property
81 testParse05 = "A AND B -C" `translatesInto` ((BConst (Positive "A") `BAnd` BConst (Positive "B")) `BAnd` BConst (Negative "C"))
83 testParse05_01 :: Property
85 "A AND (B -C)" `translatesInto` (BConst (Positive "A") `BAnd` (BConst (Positive "B") `BAnd` BConst (Negative "C")))
87 testParse06 :: Property
89 translatesInto "(A OR B OR NOT C) AND (D OR E OR F) -(G OR H OR I)"
92 ((BConst (Positive "A") `BOr` (BConst (Positive "B"))) `BOr` (BConst (Negative "C")))
94 ((BConst (Positive "D") `BOr` (BConst (Positive "E"))) `BOr` (BConst (Positive "F")))
97 ((BConst (Positive "G") `BOr` (BConst (Positive "H"))) `BOr` (BConst (Positive "I")))
101 testWordsIntoConst :: Assertion
103 let (expected :: BoolExpr Term) =
104 fromCNF (boolTreeToCNF @Term $ (BConst (Positive "The Art of Computer Programming") `BAnd` (BConst (Positive "Conceptual Mathematics"))))
105 in case parseQuery "\"The Art of Computer Programming\" AND \"Conceptual Mathematics\"" of
107 -> assertBool err False
109 -> fromCNF (getQuery x) @?= expected
111 withValidQuery :: RawQuery -> (Query -> Assertion) -> Assertion
112 withValidQuery rawQuery onValidParse = do
113 case parseQuery rawQuery of
114 Left err -> assertBool err False
115 Right x -> onValidParse x
118 testArxiv01_01 :: Assertion
119 testArxiv01_01 = withValidQuery "A AND B" $ \q ->
120 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
121 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.And (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
123 testArxiv01_02 :: Assertion
124 testArxiv01_02 = withValidQuery "\"Haskell\" AND \"Agda\"" $ \q ->
125 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
126 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.And (Arxiv.Exp $ Arxiv.Abs ["Haskell"]) ((Arxiv.Exp $ Arxiv.Abs ["Agda"]))))
128 testArxiv02 :: Assertion
129 testArxiv02 = withValidQuery "A OR B" $ \q ->
130 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
131 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.Or (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
133 testArxiv03_01 :: Assertion
134 testArxiv03_01 = withValidQuery "A AND NOT B" $ \q ->
135 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
136 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.AndNot (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
138 testArxiv03_02 :: Assertion
139 testArxiv03_02 = withValidQuery "A AND -B" $ \q ->
140 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
141 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.AndNot (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
143 -- Double negation get turned into positive.
144 testArxiv04_01 :: Assertion
145 testArxiv04_01 = withValidQuery "A AND NOT (NOT B)" $ \q ->
146 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
147 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.And (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
149 testArxiv04_02 :: Assertion
150 testArxiv04_02 = withValidQuery "A AND NOT (NOT (NOT B))" $ \q ->
151 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
152 (Arxiv.qExp (Arxiv.convertQuery q) == Just (Arxiv.AndNot (Arxiv.Exp $ Arxiv.Abs ["A"]) ((Arxiv.Exp $ Arxiv.Abs ["B"]))))
154 testArxiv05 :: Assertion
155 testArxiv05 = withValidQuery "A OR NOT B" $ \q ->
156 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
157 (Arxiv.qExp (Arxiv.convertQuery q) == Just (
158 Arxiv.Or (Arxiv.Exp $ Arxiv.Abs ["A"])
159 (Arxiv.AndNot (Arxiv.Exp $ Arxiv.Abs ["B"]) (Arxiv.Exp $ Arxiv.Abs ["B"]))
163 testArxiv06 :: Assertion
164 testArxiv06 = withValidQuery "-A" $ \q ->
165 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
166 (Arxiv.qExp (Arxiv.convertQuery q) == Just (
167 Arxiv.AndNot (Arxiv.Exp $ Arxiv.Abs ["A"]) (Arxiv.Exp $ Arxiv.Abs ["A"])
175 testPubMed01 :: Assertion
176 testPubMed01 = withValidQuery "A" $ \q ->
177 assertBool ("Query not converted into expression: " <> show @(BoolExpr Term) (fromCNF $ getQuery q))
178 (Pubmed.getESearch (Pubmed.convertQuery q) == "%23A")
180 testPubMed02_01 :: Assertion
181 testPubMed02_01 = withValidQuery "-A" $ \q -> Pubmed.getESearch (Pubmed.convertQuery q) @?= "%23NOT+A"
183 testPubMed02_02 :: Assertion
184 testPubMed02_02 = withValidQuery "NOT A" $ \q -> Pubmed.getESearch (Pubmed.convertQuery q) @?= "%23NOT+A"
186 testPubMed02_03 :: Assertion
187 testPubMed02_03 = withValidQuery "NOT (NOT A)" $ \q -> Pubmed.getESearch (Pubmed.convertQuery q) @?= "%23A"
189 testPubMed03 :: Assertion
190 testPubMed03 = withValidQuery "\"Haskell\" AND \"Idris\"" $ \q ->
191 Pubmed.getESearch (Pubmed.convertQuery q) @?= "%23Haskell+AND+Idris"
193 testPubMed04 :: Assertion
194 testPubMed04 = withValidQuery "A OR B" $ \q ->
195 Pubmed.getESearch (Pubmed.convertQuery q) @?= "%23A+OR+B"