Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / Query / Filter.hs @ b54ecf12

History | View | Annotate | Download (7.7 kB)

1 90171729 Iustin Pop
{-# LANGUAGE TemplateHaskell #-}
2 90171729 Iustin Pop
{-# OPTIONS_GHC -fno-warn-orphans #-}
3 90171729 Iustin Pop
4 90171729 Iustin Pop
{-| Unittests for ganeti-htools.
5 90171729 Iustin Pop
6 90171729 Iustin Pop
-}
7 90171729 Iustin Pop
8 90171729 Iustin Pop
{-
9 90171729 Iustin Pop
10 90171729 Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
11 90171729 Iustin Pop
12 90171729 Iustin Pop
This program is free software; you can redistribute it and/or modify
13 90171729 Iustin Pop
it under the terms of the GNU General Public License as published by
14 90171729 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
15 90171729 Iustin Pop
(at your option) any later version.
16 90171729 Iustin Pop
17 90171729 Iustin Pop
This program is distributed in the hope that it will be useful, but
18 90171729 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
19 90171729 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 90171729 Iustin Pop
General Public License for more details.
21 90171729 Iustin Pop
22 90171729 Iustin Pop
You should have received a copy of the GNU General Public License
23 90171729 Iustin Pop
along with this program; if not, write to the Free Software
24 90171729 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 90171729 Iustin Pop
02110-1301, USA.
26 90171729 Iustin Pop
27 90171729 Iustin Pop
-}
28 90171729 Iustin Pop
29 90171729 Iustin Pop
module Test.Ganeti.Query.Filter (testQuery_Filter) where
30 90171729 Iustin Pop
31 90171729 Iustin Pop
import Test.QuickCheck hiding (Result)
32 90171729 Iustin Pop
import Test.QuickCheck.Monadic
33 90171729 Iustin Pop
34 90171729 Iustin Pop
import qualified Data.Map as Map
35 90171729 Iustin Pop
import Data.List
36 90171729 Iustin Pop
import Text.JSON (showJSON)
37 90171729 Iustin Pop
38 90171729 Iustin Pop
import Test.Ganeti.TestHelper
39 90171729 Iustin Pop
import Test.Ganeti.TestCommon
40 90171729 Iustin Pop
import Test.Ganeti.Objects (genEmptyCluster)
41 90171729 Iustin Pop
42 90171729 Iustin Pop
import Ganeti.BasicTypes
43 90171729 Iustin Pop
import Ganeti.JSON
44 90171729 Iustin Pop
import Ganeti.Objects
45 b3d17f52 Iustin Pop
import Ganeti.Query.Filter
46 90171729 Iustin Pop
import Ganeti.Query.Language
47 90171729 Iustin Pop
import Ganeti.Query.Query
48 1fc3812c Iustin Pop
import Ganeti.Utils (niceSort)
49 90171729 Iustin Pop
50 90171729 Iustin Pop
-- * Helpers
51 90171729 Iustin Pop
52 90171729 Iustin Pop
-- | Run a query and check that we got a specific response.
53 90171729 Iustin Pop
checkQueryResults :: ConfigData -> Query -> String
54 90171729 Iustin Pop
                  -> [[ResultEntry]] -> Property
55 90171729 Iustin Pop
checkQueryResults cfg qr descr expected = monadicIO $ do
56 fa2c927c Agata Murawska
  result <- run (query cfg False qr) >>= resultProp
57 90171729 Iustin Pop
  stop $ printTestCase ("Inconsistent results in " ++ descr)
58 90171729 Iustin Pop
         (qresData result ==? expected)
59 90171729 Iustin Pop
60 90171729 Iustin Pop
-- | Makes a node name query, given a filter.
61 90171729 Iustin Pop
makeNodeQuery :: Filter FilterField -> Query
62 1283cc38 Iustin Pop
makeNodeQuery = Query (ItemTypeOpCode QRNode) ["name"]
63 90171729 Iustin Pop
64 90171729 Iustin Pop
-- | Checks if a given operation failed.
65 90171729 Iustin Pop
expectBadQuery :: ConfigData -> Query -> String -> Property
66 90171729 Iustin Pop
expectBadQuery cfg qr descr = monadicIO $ do
67 fa2c927c Agata Murawska
  result <- run (query cfg False qr)
68 90171729 Iustin Pop
  case result of
69 90171729 Iustin Pop
    Bad _ -> return ()
70 90171729 Iustin Pop
    Ok a  -> stop . failTest $ "Expected failure in " ++ descr ++
71 90171729 Iustin Pop
                               " but got " ++ show a
72 90171729 Iustin Pop
73 2d52359b Iustin Pop
-- | A helper to construct a list of results from an expected names list.
74 2d52359b Iustin Pop
namesToResult :: [String] -> [[ResultEntry]]
75 2d52359b Iustin Pop
namesToResult = map ((:[]) . ResultEntry RSNormal . Just . showJSON)
76 2d52359b Iustin Pop
77 2d52359b Iustin Pop
-- | Generates a cluster and returns its node names too.
78 2d52359b Iustin Pop
genClusterNames :: Int -> Int -> Gen (ConfigData, [String])
79 2d52359b Iustin Pop
genClusterNames min_nodes max_nodes = do
80 2d52359b Iustin Pop
  numnodes <- choose (min_nodes, max_nodes)
81 2d52359b Iustin Pop
  cfg <- genEmptyCluster numnodes
82 2d52359b Iustin Pop
  return (cfg, niceSort . Map.keys . fromContainer $ configNodes cfg)
83 2d52359b Iustin Pop
84 90171729 Iustin Pop
-- * Test cases
85 90171729 Iustin Pop
86 90171729 Iustin Pop
-- | Tests single node filtering: eq should return it, and (lt and gt)
87 90171729 Iustin Pop
-- should fail.
88 90171729 Iustin Pop
prop_node_single_filter :: Property
89 90171729 Iustin Pop
prop_node_single_filter =
90 2d52359b Iustin Pop
  forAll (genClusterNames 1 maxNodes) $ \(cfg, allnodes) ->
91 90171729 Iustin Pop
  forAll (elements allnodes) $ \nname ->
92 90171729 Iustin Pop
  let fvalue = QuotedString nname
93 90171729 Iustin Pop
      buildflt n = n "name" fvalue
94 2d52359b Iustin Pop
      expsingle = namesToResult [nname]
95 90171729 Iustin Pop
      othernodes = nname `delete` allnodes
96 2d52359b Iustin Pop
      expnot = namesToResult othernodes
97 90171729 Iustin Pop
      test_query = checkQueryResults cfg . makeNodeQuery
98 90171729 Iustin Pop
  in conjoin
99 90171729 Iustin Pop
       [ test_query (buildflt EQFilter) "single-name 'EQ' filter" expsingle
100 90171729 Iustin Pop
       , test_query (NotFilter (buildflt EQFilter))
101 90171729 Iustin Pop
         "single-name 'NOT EQ' filter" expnot
102 90171729 Iustin Pop
       , test_query (AndFilter [buildflt LTFilter, buildflt GTFilter])
103 90171729 Iustin Pop
         "single-name 'AND [LT,GT]' filter" []
104 90171729 Iustin Pop
       , test_query (AndFilter [buildflt LEFilter, buildflt GEFilter])
105 90171729 Iustin Pop
         "single-name 'And [LE,GE]' filter" expsingle
106 90171729 Iustin Pop
       ]
107 90171729 Iustin Pop
108 90171729 Iustin Pop
-- | Tests node filtering based on name equality: many 'OrFilter'
109 90171729 Iustin Pop
-- should return all results combined, many 'AndFilter' together
110 90171729 Iustin Pop
-- should return nothing. Note that we need at least 2 nodes so that
111 90171729 Iustin Pop
-- the 'AndFilter' case breaks.
112 90171729 Iustin Pop
prop_node_many_filter :: Property
113 90171729 Iustin Pop
prop_node_many_filter =
114 2d52359b Iustin Pop
  forAll (genClusterNames 2 maxNodes) $ \(cfg, nnames) ->
115 2d52359b Iustin Pop
  let eqfilter = map (EQFilter "name" . QuotedString) nnames
116 2d52359b Iustin Pop
      alln = namesToResult nnames
117 90171729 Iustin Pop
      test_query = checkQueryResults cfg . makeNodeQuery
118 90171729 Iustin Pop
      num_zero = NumericValue 0
119 90171729 Iustin Pop
  in conjoin
120 90171729 Iustin Pop
     [ test_query (OrFilter eqfilter) "all nodes 'Or' name filter" alln
121 90171729 Iustin Pop
     , test_query (AndFilter eqfilter) "all nodes 'And' name filter" []
122 90171729 Iustin Pop
     -- this next test works only because genEmptyCluster generates a
123 90171729 Iustin Pop
     -- cluster with no instances
124 90171729 Iustin Pop
     , test_query (EQFilter "pinst_cnt" num_zero) "pinst_cnt 'Eq' 0" alln
125 90171729 Iustin Pop
     , test_query (GTFilter "sinst_cnt" num_zero) "sinst_cnt 'GT' 0" []
126 90171729 Iustin Pop
     ]
127 90171729 Iustin Pop
128 2d52359b Iustin Pop
-- | Tests name ordering consistency: requesting a 'simple filter'
129 2d52359b Iustin Pop
-- results in identical name ordering as the wanted names, requesting
130 2d52359b Iustin Pop
-- a more complex filter results in a niceSort-ed order.
131 2d52359b Iustin Pop
prop_node_name_ordering :: Property
132 2d52359b Iustin Pop
prop_node_name_ordering =
133 2d52359b Iustin Pop
  forAll (genClusterNames 2 6) $ \(cfg, nnames) ->
134 2d52359b Iustin Pop
  forAll (elements (subsequences nnames)) $ \sorted_nodes ->
135 2d52359b Iustin Pop
  forAll (elements (permutations sorted_nodes)) $ \chosen_nodes ->
136 2d52359b Iustin Pop
  let orfilter = OrFilter $ map (EQFilter "name" . QuotedString) chosen_nodes
137 2d52359b Iustin Pop
      alln = namesToResult chosen_nodes
138 2d52359b Iustin Pop
      all_sorted = namesToResult $ niceSort chosen_nodes
139 2d52359b Iustin Pop
      test_query = checkQueryResults cfg . makeNodeQuery
140 2d52359b Iustin Pop
  in conjoin
141 2d52359b Iustin Pop
     [ test_query orfilter "simple filter/requested" alln
142 2d52359b Iustin Pop
     , test_query (AndFilter [orfilter]) "complex filter/sorted" all_sorted
143 2d52359b Iustin Pop
     ]
144 2d52359b Iustin Pop
145 90171729 Iustin Pop
-- | Tests node regex filtering. This is a very basic test :(
146 90171729 Iustin Pop
prop_node_regex_filter :: Property
147 90171729 Iustin Pop
prop_node_regex_filter =
148 2d52359b Iustin Pop
  forAll (genClusterNames 0 maxNodes) $ \(cfg, nnames) ->
149 2d52359b Iustin Pop
  case mkRegex ".*"::Result FilterRegex of
150 2d52359b Iustin Pop
    Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
151 2d52359b Iustin Pop
    Ok rx ->
152 2d52359b Iustin Pop
      checkQueryResults cfg (makeNodeQuery (RegexpFilter "name" rx))
153 2d52359b Iustin Pop
        "rows for all nodes regexp filter" $ namesToResult nnames
154 90171729 Iustin Pop
155 90171729 Iustin Pop
-- | Tests node regex filtering. This is a very basic test :(
156 90171729 Iustin Pop
prop_node_bad_filter :: String -> Int -> Property
157 90171729 Iustin Pop
prop_node_bad_filter rndname rndint =
158 2d52359b Iustin Pop
  forAll (genClusterNames 1 maxNodes) $ \(cfg, _) ->
159 2d52359b Iustin Pop
  let test_query = expectBadQuery cfg . makeNodeQuery
160 90171729 Iustin Pop
      string_value = QuotedString rndname
161 90171729 Iustin Pop
      numeric_value = NumericValue $ fromIntegral rndint
162 2d52359b Iustin Pop
  in case mkRegex ".*"::Result FilterRegex of
163 90171729 Iustin Pop
       Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
164 90171729 Iustin Pop
       Ok rx ->
165 90171729 Iustin Pop
         conjoin
166 90171729 Iustin Pop
           [ test_query (RegexpFilter "offline" rx)
167 90171729 Iustin Pop
             "regex filter against boolean field"
168 90171729 Iustin Pop
           , test_query (EQFilter "name" numeric_value)
169 90171729 Iustin Pop
             "numeric value eq against string field"
170 90171729 Iustin Pop
           , test_query (TrueFilter "name")
171 90171729 Iustin Pop
             "true filter against string field"
172 90171729 Iustin Pop
           , test_query (EQFilter "offline" string_value)
173 90171729 Iustin Pop
             "quoted string eq against boolean field"
174 90171729 Iustin Pop
           , test_query (ContainsFilter "name" string_value)
175 90171729 Iustin Pop
             "quoted string in non-list field"
176 90171729 Iustin Pop
           , test_query (ContainsFilter "name" numeric_value)
177 90171729 Iustin Pop
             "numeric value in non-list field"
178 90171729 Iustin Pop
           ]
179 90171729 Iustin Pop
180 b3d17f52 Iustin Pop
-- | Tests make simple filter.
181 b3d17f52 Iustin Pop
prop_makeSimpleFilter :: Property
182 b3d17f52 Iustin Pop
prop_makeSimpleFilter =
183 5006418e Iustin Pop
  forAll (resize 10 $ listOf1 genName) $ \names ->
184 037762a9 Iustin Pop
  forAll (resize 10 $ listOf1 arbitrary) $ \ids ->
185 5006418e Iustin Pop
  forAll genName $ \namefield ->
186 b3d17f52 Iustin Pop
  conjoin [ printTestCase "test expected names" $
187 037762a9 Iustin Pop
              makeSimpleFilter namefield (map Left names) ==?
188 b3d17f52 Iustin Pop
              OrFilter (map (EQFilter namefield . QuotedString) names)
189 037762a9 Iustin Pop
          , printTestCase "test expected IDs" $
190 037762a9 Iustin Pop
              makeSimpleFilter namefield (map Right ids) ==?
191 037762a9 Iustin Pop
              OrFilter (map (EQFilter namefield . NumericValue) ids)
192 b3d17f52 Iustin Pop
          , printTestCase "test empty names" $
193 b3d17f52 Iustin Pop
              makeSimpleFilter namefield [] ==? EmptyFilter
194 b3d17f52 Iustin Pop
          ]
195 b3d17f52 Iustin Pop
196 90171729 Iustin Pop
testSuite "Query/Filter"
197 90171729 Iustin Pop
  [ 'prop_node_single_filter
198 90171729 Iustin Pop
  , 'prop_node_many_filter
199 2d52359b Iustin Pop
  , 'prop_node_name_ordering
200 90171729 Iustin Pop
  , 'prop_node_regex_filter
201 90171729 Iustin Pop
  , 'prop_node_bad_filter
202 b3d17f52 Iustin Pop
  , 'prop_makeSimpleFilter
203 90171729 Iustin Pop
  ]