1 {-# LANGUAGE TemplateHaskell #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 {-| Unittests for ganeti-htools.
10 Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29 module Test.Ganeti.Query.Filter (testQuery_Filter) where
31 import Test.QuickCheck hiding (Result)
32 import Test.QuickCheck.Monadic
34 import qualified Data.Map as Map
36 import Text.JSON (showJSON)
38 import Test.Ganeti.TestHelper
39 import Test.Ganeti.TestCommon
40 import Test.Ganeti.Objects (genEmptyCluster)
42 import Ganeti.BasicTypes
45 import Ganeti.Query.Language
46 import Ganeti.Query.Query
50 -- | Run a query and check that we got a specific response.
51 checkQueryResults :: ConfigData -> Query -> String
52 -> [[ResultEntry]] -> Property
53 checkQueryResults cfg qr descr expected = monadicIO $ do
54 result <- run (query cfg qr) >>= resultProp
55 stop $ printTestCase ("Inconsistent results in " ++ descr)
56 (qresData result ==? expected)
58 -- | Makes a node name query, given a filter.
59 makeNodeQuery :: Filter FilterField -> Query
60 makeNodeQuery = Query QRNode ["name"]
62 -- | Checks if a given operation failed.
63 expectBadQuery :: ConfigData -> Query -> String -> Property
64 expectBadQuery cfg qr descr = monadicIO $ do
65 result <- run (query cfg qr)
68 Ok a -> stop . failTest $ "Expected failure in " ++ descr ++
73 -- | Tests single node filtering: eq should return it, and (lt and gt)
75 prop_node_single_filter :: Property
76 prop_node_single_filter =
77 forAll (choose (1, maxNodes)) $ \numnodes ->
78 forAll (genEmptyCluster numnodes) $ \cfg ->
79 let allnodes = Map.keys . fromContainer $ configNodes cfg in
80 forAll (elements allnodes) $ \nname ->
81 let fvalue = QuotedString nname
82 buildflt n = n "name" fvalue
83 expsingle = [[ResultEntry RSNormal (Just (showJSON nname))]]
84 othernodes = nname `delete` allnodes
85 expnot = map ((:[]) . ResultEntry RSNormal . Just . showJSON) othernodes
86 test_query = checkQueryResults cfg . makeNodeQuery
88 [ test_query (buildflt EQFilter) "single-name 'EQ' filter" expsingle
89 , test_query (NotFilter (buildflt EQFilter))
90 "single-name 'NOT EQ' filter" expnot
91 , test_query (AndFilter [buildflt LTFilter, buildflt GTFilter])
92 "single-name 'AND [LT,GT]' filter" []
93 , test_query (AndFilter [buildflt LEFilter, buildflt GEFilter])
94 "single-name 'And [LE,GE]' filter" expsingle
97 -- | Tests node filtering based on name equality: many 'OrFilter'
98 -- should return all results combined, many 'AndFilter' together
99 -- should return nothing. Note that we need at least 2 nodes so that
100 -- the 'AndFilter' case breaks.
101 prop_node_many_filter :: Property
102 prop_node_many_filter =
103 forAll (choose (2, maxNodes)) $ \numnodes ->
104 forAll (genEmptyCluster numnodes) $ \cfg ->
105 let nnames = Map.keys . fromContainer $ configNodes cfg
106 eqfilter = map (EQFilter "name" . QuotedString) nnames
107 alln = map ((:[]) . ResultEntry RSNormal . Just . showJSON) nnames
108 test_query = checkQueryResults cfg . makeNodeQuery
109 num_zero = NumericValue 0
111 [ test_query (OrFilter eqfilter) "all nodes 'Or' name filter" alln
112 , test_query (AndFilter eqfilter) "all nodes 'And' name filter" []
113 -- this next test works only because genEmptyCluster generates a
114 -- cluster with no instances
115 , test_query (EQFilter "pinst_cnt" num_zero) "pinst_cnt 'Eq' 0" alln
116 , test_query (GTFilter "sinst_cnt" num_zero) "sinst_cnt 'GT' 0" []
119 -- | Tests node regex filtering. This is a very basic test :(
120 prop_node_regex_filter :: Property
121 prop_node_regex_filter =
122 forAll (choose (0, maxNodes)) $ \numnodes ->
123 forAll (genEmptyCluster numnodes) $ \cfg ->
124 let nnames = Map.keys . fromContainer $ configNodes cfg
125 expected = map ((:[]) . ResultEntry RSNormal . Just . showJSON) nnames
126 regex = mkRegex ".*"::Result FilterRegex
128 Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
130 checkQueryResults cfg (makeNodeQuery (RegexpFilter "name" rx))
131 "Inconsistent result rows for all nodes regexp filter"
134 -- | Tests node regex filtering. This is a very basic test :(
135 prop_node_bad_filter :: String -> Int -> Property
136 prop_node_bad_filter rndname rndint =
137 forAll (choose (1, maxNodes)) $ \numnodes ->
138 forAll (genEmptyCluster numnodes) $ \cfg ->
139 let regex = mkRegex ".*"::Result FilterRegex
140 test_query = expectBadQuery cfg . makeNodeQuery
141 string_value = QuotedString rndname
142 numeric_value = NumericValue $ fromIntegral rndint
144 Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
147 [ test_query (RegexpFilter "offline" rx)
148 "regex filter against boolean field"
149 , test_query (EQFilter "name" numeric_value)
150 "numeric value eq against string field"
151 , test_query (TrueFilter "name")
152 "true filter against string field"
153 , test_query (EQFilter "offline" string_value)
154 "quoted string eq against boolean field"
155 , test_query (ContainsFilter "name" string_value)
156 "quoted string in non-list field"
157 , test_query (ContainsFilter "name" numeric_value)
158 "numeric value in non-list field"
161 testSuite "Query/Filter"
162 [ 'prop_node_single_filter
163 , 'prop_node_many_filter
164 , 'prop_node_regex_filter
165 , 'prop_node_bad_filter