Statistics
| Branch: | Tag: | Revision:

root / htest / Test / Ganeti / Query / Filter.hs @ b3d17f52

History | View | Annotate | Download (6.7 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.Filter
46
import Ganeti.Query.Language
47
import Ganeti.Query.Query
48
import Ganeti.Utils (niceSort)
49

    
50
-- * Helpers
51

    
52
-- | Run a query and check that we got a specific response.
53
checkQueryResults :: ConfigData -> Query -> String
54
                  -> [[ResultEntry]] -> Property
55
checkQueryResults cfg qr descr expected = monadicIO $ do
56
  result <- run (query cfg False qr) >>= resultProp
57
  stop $ printTestCase ("Inconsistent results in " ++ descr)
58
         (qresData result ==? expected)
59

    
60
-- | Makes a node name query, given a filter.
61
makeNodeQuery :: Filter FilterField -> Query
62
makeNodeQuery = Query QRNode ["name"]
63

    
64
-- | Checks if a given operation failed.
65
expectBadQuery :: ConfigData -> Query -> String -> Property
66
expectBadQuery cfg qr descr = monadicIO $ do
67
  result <- run (query cfg False qr)
68
  case result of
69
    Bad _ -> return ()
70
    Ok a  -> stop . failTest $ "Expected failure in " ++ descr ++
71
                               " but got " ++ show a
72

    
73
-- * Test cases
74

    
75
-- | Tests single node filtering: eq should return it, and (lt and gt)
76
-- should fail.
77
prop_node_single_filter :: Property
78
prop_node_single_filter =
79
  forAll (choose (1, maxNodes)) $ \numnodes ->
80
  forAll (genEmptyCluster numnodes) $ \cfg ->
81
  let allnodes = niceSort . Map.keys . fromContainer $ configNodes cfg in
82
  forAll (elements allnodes) $ \nname ->
83
  let fvalue = QuotedString nname
84
      buildflt n = n "name" fvalue
85
      expsingle = [[ResultEntry RSNormal (Just (showJSON nname))]]
86
      othernodes = nname `delete` allnodes
87
      expnot = map ((:[]) . ResultEntry RSNormal . Just . showJSON) othernodes
88
      test_query = checkQueryResults cfg . makeNodeQuery
89
  in conjoin
90
       [ test_query (buildflt EQFilter) "single-name 'EQ' filter" expsingle
91
       , test_query (NotFilter (buildflt EQFilter))
92
         "single-name 'NOT EQ' filter" expnot
93
       , test_query (AndFilter [buildflt LTFilter, buildflt GTFilter])
94
         "single-name 'AND [LT,GT]' filter" []
95
       , test_query (AndFilter [buildflt LEFilter, buildflt GEFilter])
96
         "single-name 'And [LE,GE]' filter" expsingle
97
       ]
98

    
99
-- | Tests node filtering based on name equality: many 'OrFilter'
100
-- should return all results combined, many 'AndFilter' together
101
-- should return nothing. Note that we need at least 2 nodes so that
102
-- the 'AndFilter' case breaks.
103
prop_node_many_filter :: Property
104
prop_node_many_filter =
105
  forAll (choose (2, maxNodes)) $ \numnodes ->
106
  forAll (genEmptyCluster numnodes) $ \cfg ->
107
  let nnames = niceSort . Map.keys . fromContainer $ configNodes cfg
108
      eqfilter = map (EQFilter "name" . QuotedString) nnames
109
      alln = map ((:[]) . ResultEntry RSNormal . Just . showJSON) nnames
110
      test_query = checkQueryResults cfg . makeNodeQuery
111
      num_zero = NumericValue 0
112
  in conjoin
113
     [ test_query (OrFilter eqfilter) "all nodes 'Or' name filter" alln
114
     , test_query (AndFilter eqfilter) "all nodes 'And' name filter" []
115
     -- this next test works only because genEmptyCluster generates a
116
     -- cluster with no instances
117
     , test_query (EQFilter "pinst_cnt" num_zero) "pinst_cnt 'Eq' 0" alln
118
     , test_query (GTFilter "sinst_cnt" num_zero) "sinst_cnt 'GT' 0" []
119
     ]
120

    
121
-- | Tests node regex filtering. This is a very basic test :(
122
prop_node_regex_filter :: Property
123
prop_node_regex_filter =
124
  forAll (choose (0, maxNodes)) $ \numnodes ->
125
  forAll (genEmptyCluster numnodes) $ \cfg ->
126
  let nnames = niceSort . Map.keys . fromContainer $ configNodes cfg
127
      expected = map ((:[]) . ResultEntry RSNormal . Just . showJSON) nnames
128
      regex = mkRegex ".*"::Result FilterRegex
129
  in case regex of
130
       Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
131
       Ok rx ->
132
         checkQueryResults cfg (makeNodeQuery (RegexpFilter "name" rx))
133
           "rows for all nodes regexp filter"
134
           expected
135

    
136
-- | Tests node regex filtering. This is a very basic test :(
137
prop_node_bad_filter :: String -> Int -> Property
138
prop_node_bad_filter rndname rndint =
139
  forAll (choose (1, maxNodes)) $ \numnodes ->
140
  forAll (genEmptyCluster numnodes) $ \cfg ->
141
  let regex = mkRegex ".*"::Result FilterRegex
142
      test_query = expectBadQuery cfg . makeNodeQuery
143
      string_value = QuotedString rndname
144
      numeric_value = NumericValue $ fromIntegral rndint
145
  in case regex of
146
       Bad msg -> failTest $ "Can't build regex?! Error: " ++ msg
147
       Ok rx ->
148
         conjoin
149
           [ test_query (RegexpFilter "offline" rx)
150
             "regex filter against boolean field"
151
           , test_query (EQFilter "name" numeric_value)
152
             "numeric value eq against string field"
153
           , test_query (TrueFilter "name")
154
             "true filter against string field"
155
           , test_query (EQFilter "offline" string_value)
156
             "quoted string eq against boolean field"
157
           , test_query (ContainsFilter "name" string_value)
158
             "quoted string in non-list field"
159
           , test_query (ContainsFilter "name" numeric_value)
160
             "numeric value in non-list field"
161
           ]
162

    
163
-- | Tests make simple filter.
164
prop_makeSimpleFilter :: Property
165
prop_makeSimpleFilter =
166
  forAll (resize 10 $ listOf1 getName) $ \names ->
167
  forAll getName $ \namefield ->
168
  conjoin [ printTestCase "test expected names" $
169
              makeSimpleFilter namefield names ==?
170
              OrFilter (map (EQFilter namefield . QuotedString) names)
171
          , printTestCase "test empty names" $
172
              makeSimpleFilter namefield [] ==? EmptyFilter
173
          ]
174

    
175
testSuite "Query/Filter"
176
  [ 'prop_node_single_filter
177
  , 'prop_node_many_filter
178
  , 'prop_node_regex_filter
179
  , 'prop_node_bad_filter
180
  , 'prop_makeSimpleFilter
181
  ]