root / htest / Test / Ganeti / Query / Filter.hs @ 90171729
History | View | Annotate | Download (6.2 kB)
1 |
{-# LANGUAGE TemplateHaskell #-} |
---|---|
2 |
{-# OPTIONS_GHC -fno-warn-orphans #-} |
3 |
|
4 |
{-| Unittests for ganeti-htools. |
5 |
|
6 |
-} |
7 |
|
8 |
{- |
9 |
|
10 |
Copyright (C) 2009, 2010, 2011, 2012 Google Inc. |
11 |
|
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. |
16 |
|
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. |
21 |
|
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 |
25 |
02110-1301, USA. |
26 |
|
27 |
-} |
28 |
|
29 |
module Test.Ganeti.Query.Filter (testQuery_Filter) where |
30 |
|
31 |
import Test.QuickCheck hiding (Result) |
32 |
import Test.QuickCheck.Monadic |
33 |
|
34 |
import qualified Data.Map as Map |
35 |
import Data.List |
36 |
import Text.JSON (showJSON) |
37 |
|
38 |
import Test.Ganeti.TestHelper |
39 |
import Test.Ganeti.TestCommon |
40 |
import Test.Ganeti.Objects (genEmptyCluster) |
41 |
|
42 |
import Ganeti.BasicTypes |
43 |
import Ganeti.JSON |
44 |
import Ganeti.Objects |
45 |
import Ganeti.Query.Language |
46 |
import Ganeti.Query.Query |
47 |
|
48 |
-- * Helpers |
49 |
|
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) |
57 |
|
58 |
-- | Makes a node name query, given a filter. |
59 |
makeNodeQuery :: Filter FilterField -> Query |
60 |
makeNodeQuery = Query QRNode ["name"] |
61 |
|
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) |
66 |
case result of |
67 |
Bad _ -> return () |
68 |
Ok a -> stop . failTest $ "Expected failure in " ++ descr ++ |
69 |
" but got " ++ show a |
70 |
|
71 |
-- * Test cases |
72 |
|
73 |
-- | Tests single node filtering: eq should return it, and (lt and gt) |
74 |
-- should fail. |
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 |
87 |
in conjoin |
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 |
95 |
] |
96 |
|
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 |
110 |
in conjoin |
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" [] |
117 |
] |
118 |
|
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 |
127 |
in case regex of |
128 |
Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg |
129 |
Ok rx -> |
130 |
checkQueryResults cfg (makeNodeQuery (RegexpFilter "name" rx)) |
131 |
"Inconsistent result rows for all nodes regexp filter" |
132 |
expected |
133 |
|
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 |
143 |
in case regex of |
144 |
Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg |
145 |
Ok rx -> |
146 |
conjoin |
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" |
159 |
] |
160 |
|
161 |
testSuite "Query/Filter" |
162 |
[ 'prop_node_single_filter |
163 |
, 'prop_node_many_filter |
164 |
, 'prop_node_regex_filter |
165 |
, 'prop_node_bad_filter |
166 |
] |