17944: Vocabulary loading, monitoring and checking on several object types.
[arvados.git] / sdk / go / arvados / vocabulary_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "encoding/json"
9
10         check "gopkg.in/check.v1"
11 )
12
13 type VocabularySuite struct {
14         testVoc *Vocabulary
15 }
16
17 var _ = check.Suite(&VocabularySuite{})
18
19 func (s *VocabularySuite) SetUpTest(c *check.C) {
20         s.testVoc = &Vocabulary{
21                 reservedTagKeys: map[string]bool{
22                         "reservedKey": true,
23                 },
24                 StrictTags: false,
25                 Tags: map[string]VocabularyTag{
26                         "IDTAGANIMALS": {
27                                 Strict: false,
28                                 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
29                                 Values: map[string]VocabularyTagValue{
30                                         "IDVALANIMAL1": {
31                                                 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Homo sapiens"}},
32                                         },
33                                         "IDVALANIMAL2": {
34                                                 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Loxodonta"}},
35                                         },
36                                 },
37                         },
38                         "IDTAGIMPORTANCE": {
39                                 Strict: true,
40                                 Labels: []VocabularyLabel{{Label: "Importance"}, {Label: "Priority"}},
41                                 Values: map[string]VocabularyTagValue{
42                                         "IDVAL3": {
43                                                 Labels: []VocabularyLabel{{Label: "Low"}, {Label: "Low priority"}},
44                                         },
45                                         "IDVAL2": {
46                                                 Labels: []VocabularyLabel{{Label: "Medium"}, {Label: "Medium priority"}},
47                                         },
48                                         "IDVAL1": {
49                                                 Labels: []VocabularyLabel{{Label: "High"}, {Label: "High priority"}},
50                                         },
51                                 },
52                         },
53                         "IDTAGCOMMENT": {
54                                 Strict: false,
55                                 Labels: []VocabularyLabel{{Label: "Comment"}},
56                         },
57                 },
58         }
59         err := s.testVoc.Validate()
60         c.Assert(err, check.IsNil)
61 }
62
63 func (s *VocabularySuite) TestCheck(c *check.C) {
64         tests := []struct {
65                 name          string
66                 strictVoc     bool
67                 props         string
68                 expectSuccess bool
69         }{
70                 // Check succeeds
71                 {"Known key, known value", false, `{"IDTAGANIMALS":"IDVALANIMAL1"}`, true},
72                 {"Unknown non-alias key on non-strict vocabulary", false, `{"foo":"bar"}`, true},
73                 {"Known non-strict key, unknown non-alias value", false, `{"IDTAGANIMALS":"IDVALANIMAL3"}`, true},
74                 {"Undefined but reserved key on strict vocabulary", true, `{"reservedKey":"bar"}`, true},
75                 {"Known key, list of known values", false, `{"IDTAGANIMALS":["IDVALANIMAL1","IDVALANIMAL2"]}`, true},
76                 {"Known non-strict key, list of unknown non-alias values", false, `{"IDTAGCOMMENT":["hello world","lorem ipsum"]}`, true},
77                 // Check fails
78                 {"Unknown non-alias key on strict vocabulary", true, `{"foo":"bar"}`, false},
79                 {"Known non-strict key, known value alias", false, `{"IDTAGANIMALS":"Loxodonta"}`, false},
80                 {"Known strict key, unknown non-alias value", false, `{"IDTAGIMPORTANCE":"Unimportant"}`, false},
81                 {"Known strict key, known value alias", false, `{"IDTAGIMPORTANCE":"High"}`, false},
82                 {"Known strict key, list of known alias values", false, `{"IDTAGIMPORTANCE":["Unimportant","High"]}`, false},
83                 {"Known strict key, list of unknown non-alias values", false, `{"IDTAGIMPORTANCE":["foo","bar"]}`, false},
84         }
85         for _, tt := range tests {
86                 c.Log(c.TestName()+" ", tt.name)
87                 s.testVoc.StrictTags = tt.strictVoc
88
89                 var data map[string]interface{}
90                 err := json.Unmarshal([]byte(tt.props), &data)
91                 c.Assert(err, check.IsNil)
92                 err = s.testVoc.Check(data)
93                 if tt.expectSuccess {
94                         c.Assert(err, check.IsNil)
95                 } else {
96                         c.Assert(err, check.NotNil)
97                 }
98         }
99 }
100
101 func (s *VocabularySuite) TestNewVocabulary(c *check.C) {
102         tests := []struct {
103                 name       string
104                 data       string
105                 isValid    bool
106                 errMatches string
107                 expect     *Vocabulary
108         }{
109                 {"Empty data", "", true, "", &Vocabulary{}},
110                 {"Invalid JSON", "foo", false, "invalid JSON format.*", nil},
111                 {"Valid, empty JSON", "{}", false, ".*doesn't match Vocabulary format.*", nil},
112                 {"Valid JSON, wrong data", `{"foo":"bar"}`, false, ".*doesn't match Vocabulary format.*", nil},
113                 {
114                         "Simple valid example",
115                         `{"tags":{
116                                 "IDTAGANIMALS":{
117                                         "strict": false,
118                                         "labels": [{"label": "Animal"}, {"label": "Creature"}],
119                                         "values": {
120                                                 "IDVALANIMAL1":{"labels":[{"label":"Human"}, {"label":"Homo sapiens"}]},
121                                                 "IDVALANIMAL2":{"labels":[{"label":"Elephant"}, {"label":"Loxodonta"}]}
122                                         }
123                                 }
124                         }}`,
125                         true, "",
126                         &Vocabulary{
127                                 reservedTagKeys: map[string]bool{
128                                         "type":                  true,
129                                         "template_uuid":         true,
130                                         "groups":                true,
131                                         "username":              true,
132                                         "image_timestamp":       true,
133                                         "docker-image-repo-tag": true,
134                                         "filters":               true,
135                                         "container_request":     true,
136                                 },
137                                 StrictTags: false,
138                                 Tags: map[string]VocabularyTag{
139                                         "IDTAGANIMALS": {
140                                                 Strict: false,
141                                                 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
142                                                 Values: map[string]VocabularyTagValue{
143                                                         "IDVALANIMAL1": {
144                                                                 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Homo sapiens"}},
145                                                         },
146                                                         "IDVALANIMAL2": {
147                                                                 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Loxodonta"}},
148                                                         },
149                                                 },
150                                         },
151                                 },
152                         },
153                 },
154                 {
155                         "Valid data, but uses reserved key",
156                         `{"tags":{
157                                 "type":{
158                                         "strict": false,
159                                         "labels": [{"label": "Type"}]
160                                 }
161                         }}`,
162                         false, "tag key.*is reserved", nil,
163                 },
164         }
165
166         for _, tt := range tests {
167                 c.Log(c.TestName()+" ", tt.name)
168                 voc, err := NewVocabulary([]byte(tt.data), []string{})
169                 if tt.isValid {
170                         c.Assert(err, check.IsNil)
171                 } else {
172                         c.Assert(err, check.NotNil)
173                         if tt.errMatches != "" {
174                                 c.Assert(err, check.ErrorMatches, tt.errMatches)
175                         }
176                 }
177                 c.Assert(voc, check.DeepEquals, tt.expect)
178         }
179 }
180
181 func (s *VocabularySuite) TestValidationErrors(c *check.C) {
182         tests := []struct {
183                 name       string
184                 voc        *Vocabulary
185                 errMatches string
186         }{
187                 {
188                         "Strict vocabulary, no keys",
189                         &Vocabulary{
190                                 StrictTags: true,
191                         },
192                         "vocabulary is strict but no tags are defined",
193                 },
194                 {
195                         "Duplicated tag keys",
196                         &Vocabulary{
197                                 StrictTags: false,
198                                 Tags: map[string]VocabularyTag{
199                                         "IDTAGANIMALS": {
200                                                 Strict: false,
201                                                 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
202                                         },
203                                         "IDTAGCOMMENT": {
204                                                 Strict: false,
205                                                 Labels: []VocabularyLabel{{Label: "Comment"}, {Label: "Animal"}},
206                                         },
207                                 },
208                         },
209                         "tag label.*for key.*already seen.*",
210                 },
211                 {
212                         "Duplicated tag values",
213                         &Vocabulary{
214                                 StrictTags: false,
215                                 Tags: map[string]VocabularyTag{
216                                         "IDTAGANIMALS": {
217                                                 Strict: false,
218                                                 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
219                                                 Values: map[string]VocabularyTagValue{
220                                                         "IDVALANIMAL1": {
221                                                                 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}},
222                                                         },
223                                                         "IDVALANIMAL2": {
224                                                                 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Mammal"}},
225                                                         },
226                                                 },
227                                         },
228                                 },
229                         },
230                         "tag value label.*for pair.*already seen.*",
231                 },
232                 {
233                         "Strict key, no values",
234                         &Vocabulary{
235                                 StrictTags: false,
236                                 Tags: map[string]VocabularyTag{
237                                         "IDTAGANIMALS": {
238                                                 Strict: true,
239                                                 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
240                                         },
241                                 },
242                         },
243                         "tag key.*is configured as strict but doesn't provide values",
244                 },
245         }
246         for _, tt := range tests {
247                 c.Log(c.TestName()+" ", tt.name)
248                 err := tt.voc.Validate()
249                 c.Assert(err, check.NotNil)
250                 c.Assert(err, check.ErrorMatches, tt.errMatches)
251         }
252 }