1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
10 check "gopkg.in/check.v1"
13 type VocabularySuite struct {
17 var _ = check.Suite(&VocabularySuite{})
19 func (s *VocabularySuite) SetUpTest(c *check.C) {
20 s.testVoc = &Vocabulary{
21 reservedTagKeys: map[string]bool{
25 Tags: map[string]VocabularyTag{
28 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
29 Values: map[string]VocabularyTagValue{
31 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Homo sapiens"}},
34 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Loxodonta"}},
40 Labels: []VocabularyLabel{{Label: "Importance"}, {Label: "Priority"}},
41 Values: map[string]VocabularyTagValue{
43 Labels: []VocabularyLabel{{Label: "Low"}, {Label: "Low priority"}},
46 Labels: []VocabularyLabel{{Label: "Medium"}, {Label: "Medium priority"}},
49 Labels: []VocabularyLabel{{Label: "High"}, {Label: "High priority"}},
55 Labels: []VocabularyLabel{{Label: "Comment"}},
59 err := s.testVoc.validate()
60 c.Assert(err, check.IsNil)
63 func (s *VocabularySuite) TestCheck(c *check.C) {
73 "Known key, known value",
75 `{"IDTAGANIMALS":"IDVALANIMAL1"}`,
80 "Unknown non-alias key on non-strict vocabulary",
87 "Known non-strict key, unknown non-alias value",
89 `{"IDTAGANIMALS":"IDVALANIMAL3"}`,
94 "Undefined but reserved key on strict vocabulary",
96 `{"reservedKey":"bar"}`,
101 "Known key, list of known values",
103 `{"IDTAGANIMALS":["IDVALANIMAL1","IDVALANIMAL2"]}`,
108 "Known non-strict key, list of unknown non-alias values",
110 `{"IDTAGCOMMENT":["hello world","lorem ipsum"]}`,
116 "Known first key & value; known 2nd key, unknown 2nd value",
118 `{"IDTAGANIMALS":"IDVALANIMAL1", "IDTAGIMPORTANCE": "blah blah"}`,
120 "tag value.*is not valid for key.*",
123 "Unknown non-alias key on strict vocabulary",
127 "tag key.*is not defined in the vocabulary",
130 "Known non-strict key, known value alias",
132 `{"IDTAGANIMALS":"Loxodonta"}`,
134 "tag value.*for key.* is an alias, must be provided as.*",
137 "Known strict key, unknown non-alias value",
139 `{"IDTAGIMPORTANCE":"Unimportant"}`,
141 "tag value.*is not valid for key.*",
144 "Known strict key, lowercase value regarded as alias",
146 `{"IDTAGIMPORTANCE":"idval1"}`,
148 "tag value.*for key.* is an alias, must be provided as.*",
151 "Known strict key, known value alias",
153 `{"IDTAGIMPORTANCE":"High"}`,
155 "tag value.* for key.*is an alias, must be provided as.*",
158 "Known strict key, list of known alias values",
160 `{"IDTAGIMPORTANCE":["High", "Low"]}`,
162 "tag value.*for key.*is an alias, must be provided as.*",
165 "Known strict key, list of unknown non-alias values",
167 `{"IDTAGIMPORTANCE":["foo","bar"]}`,
169 "tag value.*is not valid for key.*",
172 "Invalid value type",
174 `{"IDTAGANIMALS":1}`,
176 "value type for tag key.* was.*, but expected a string or list of strings",
179 "Value list of invalid type",
181 `{"IDTAGANIMALS":[1]}`,
183 "value list element type for tag key.* was.*, but expected a string",
186 for _, tt := range tests {
187 c.Log(c.TestName()+" ", tt.name)
188 s.testVoc.StrictTags = tt.strictVoc
190 var data map[string]interface{}
191 err := json.Unmarshal([]byte(tt.props), &data)
192 c.Assert(err, check.IsNil)
193 err = s.testVoc.Check(data)
194 if tt.expectSuccess {
195 c.Assert(err, check.IsNil)
197 c.Assert(err, check.NotNil)
198 c.Assert(err.Error(), check.Matches, tt.errMatches)
203 func (s *VocabularySuite) TestNewVocabulary(c *check.C) {
211 {"Empty data", "", true, "", &Vocabulary{}},
212 {"Invalid JSON", "foo", false, "invalid JSON format.*", nil},
213 {"Valid, empty JSON", "{}", false, ".*doesn't match Vocabulary format.*", nil},
214 {"Valid JSON, wrong data", `{"foo":"bar"}`, false, ".*doesn't match Vocabulary format.*", nil},
216 "Simple valid example",
220 "labels": [{"label": "Animal"}, {"label": "Creature"}],
222 "IDVALANIMAL1":{"labels":[{"label":"Human"}, {"label":"Homo sapiens"}]},
223 "IDVALANIMAL2":{"labels":[{"label":"Elephant"}, {"label":"Loxodonta"}]},
224 "DOG":{"labels":[{"label":"Dog"}, {"label":"Canis lupus familiaris"}, {"label":"dOg"}]}
230 reservedTagKeys: map[string]bool{
232 "template_uuid": true,
235 "image_timestamp": true,
236 "docker-image-repo-tag": true,
238 "container_request": true,
241 Tags: map[string]VocabularyTag{
244 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
245 Values: map[string]VocabularyTagValue{
247 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Homo sapiens"}},
250 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Loxodonta"}},
253 Labels: []VocabularyLabel{{Label: "Dog"}, {Label: "Canis lupus familiaris"}, {Label: "dOg"}},
261 "Invalid JSON error with line & column numbers",
264 "labels": [,{"label": "A label"}]
267 false, `invalid JSON format:.*\(line \d+, column \d+\)`, nil,
270 "Invalid JSON with duplicate keys",
274 "labels": [{"label": "Class", "label": "Type"}]
280 false, "(?s).*tags.type.labels.0.label\ntags.type", nil,
283 "Valid data, but uses reserved key",
287 "labels": [{"label": "Class"}]
290 false, "(?s).*tag key.*is reserved", nil,
294 for _, tt := range tests {
295 c.Log(c.TestName()+" ", tt.name)
296 voc, err := NewVocabulary([]byte(tt.data), []string{})
298 c.Assert(err, check.IsNil)
300 c.Assert(err, check.NotNil)
301 if tt.errMatches != "" {
302 c.Assert(err, check.ErrorMatches, tt.errMatches)
305 c.Assert(voc, check.DeepEquals, tt.expect)
309 func (s *VocabularySuite) TestValidationErrors(c *check.C) {
316 "Strict vocabulary, no keys",
320 []string{"vocabulary is strict but no tags are defined"},
323 "Collision between tag key and tag key label",
326 Tags: map[string]VocabularyTag{
329 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
333 Labels: []VocabularyLabel{{Label: "Comment"}, {Label: "IDTAGANIMALS"}},
337 nil, // Depending on how the map is sorted, this could be one of two errors
340 "Collision between tag key and tag key label (case-insensitive)",
343 Tags: map[string]VocabularyTag{
346 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
350 Labels: []VocabularyLabel{{Label: "Comment"}, {Label: "IdTagAnimals"}},
354 nil, // Depending on how the map is sorted, this could be one of two errors
357 "Collision between tag key labels",
360 Tags: map[string]VocabularyTag{
363 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
367 Labels: []VocabularyLabel{{Label: "Comment"}, {Label: "Animal"}},
371 []string{"(?s).*tag label.*for key.*already seen.*"},
374 "Collision between tag value and tag value label",
377 Tags: map[string]VocabularyTag{
380 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
381 Values: map[string]VocabularyTagValue{
383 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}},
386 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "IDVALANIMAL1"}},
392 nil, // Depending on how the map is sorted, this could be one of two errors
395 "Collision between tag value and tag value label (case-insensitive)",
398 Tags: map[string]VocabularyTag{
401 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
402 Values: map[string]VocabularyTagValue{
404 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}},
407 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "IDValAnimal1"}},
413 nil, // Depending on how the map is sorted, this could be one of two errors
416 "Collision between tag value labels",
419 Tags: map[string]VocabularyTag{
422 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
423 Values: map[string]VocabularyTagValue{
425 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}},
428 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Mammal"}},
434 []string{"(?s).*tag value label.*for pair.*already seen.*on value.*"},
437 "Collision between tag value labels (case-insensitive)",
440 Tags: map[string]VocabularyTag{
443 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
444 Values: map[string]VocabularyTagValue{
446 Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}},
449 Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "mAMMAL"}},
455 []string{"(?s).*tag value label.*for pair.*already seen.*on value.*"},
458 "Strict tag key, with no values",
461 Tags: map[string]VocabularyTag{
464 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
468 []string{"(?s).*tag key.*is configured as strict but doesn't provide values"},
471 "Multiple errors reported",
474 Tags: map[string]VocabularyTag{
477 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}},
480 Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Size"}},
485 "(?s).*tag key.*is configured as strict but doesn't provide values.*",
486 "(?s).*tag label.*for key.*already seen.*",
490 for _, tt := range tests {
491 c.Log(c.TestName()+" ", tt.name)
492 err := tt.voc.validate()
493 c.Assert(err, check.NotNil)
494 for _, errMatch := range tt.errMatches {
495 c.Assert(err, check.ErrorMatches, errMatch)