X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/d1af1ede3314ec5ae9b7dbbe51f8a8a7314ba651..09cbdc3074b3f1e69c9c537875146f6da0a6ed8f:/sdk/go/arvados/vocabulary_test.go diff --git a/sdk/go/arvados/vocabulary_test.go b/sdk/go/arvados/vocabulary_test.go index 4756e720e3..f31a4f984b 100644 --- a/sdk/go/arvados/vocabulary_test.go +++ b/sdk/go/arvados/vocabulary_test.go @@ -6,6 +6,8 @@ package arvados import ( "encoding/json" + "regexp" + "strings" check "gopkg.in/check.v1" ) @@ -56,7 +58,7 @@ func (s *VocabularySuite) SetUpTest(c *check.C) { }, }, } - err := s.testVoc.validate() + _, err := s.testVoc.validate() c.Assert(err, check.IsNil) } @@ -66,22 +68,122 @@ func (s *VocabularySuite) TestCheck(c *check.C) { strictVoc bool props string expectSuccess bool + errMatches string }{ // Check succeeds - {"Known key, known value", false, `{"IDTAGANIMALS":"IDVALANIMAL1"}`, true}, - {"Unknown non-alias key on non-strict vocabulary", false, `{"foo":"bar"}`, true}, - {"Known non-strict key, unknown non-alias value", false, `{"IDTAGANIMALS":"IDVALANIMAL3"}`, true}, - {"Undefined but reserved key on strict vocabulary", true, `{"reservedKey":"bar"}`, true}, - {"Known key, list of known values", false, `{"IDTAGANIMALS":["IDVALANIMAL1","IDVALANIMAL2"]}`, true}, - {"Known non-strict key, list of unknown non-alias values", false, `{"IDTAGCOMMENT":["hello world","lorem ipsum"]}`, true}, + { + "Known key, known value", + false, + `{"IDTAGANIMALS":"IDVALANIMAL1"}`, + true, + "", + }, + { + "Unknown non-alias key on non-strict vocabulary", + false, + `{"foo":"bar"}`, + true, + "", + }, + { + "Known non-strict key, unknown non-alias value", + false, + `{"IDTAGANIMALS":"IDVALANIMAL3"}`, + true, + "", + }, + { + "Undefined but reserved key on strict vocabulary", + true, + `{"reservedKey":"bar"}`, + true, + "", + }, + { + "Known key, list of known values", + false, + `{"IDTAGANIMALS":["IDVALANIMAL1","IDVALANIMAL2"]}`, + true, + "", + }, + { + "Known non-strict key, list of unknown non-alias values", + false, + `{"IDTAGCOMMENT":["hello world","lorem ipsum"]}`, + true, + "", + }, // Check fails - {"Known first key & value; known 2nd key, unknown 2nd value", false, `{"IDTAGANIMALS":"IDVALANIMAL1", "IDTAGIMPORTANCE": "blah blah"}`, false}, - {"Unknown non-alias key on strict vocabulary", true, `{"foo":"bar"}`, false}, - {"Known non-strict key, known value alias", false, `{"IDTAGANIMALS":"Loxodonta"}`, false}, - {"Known strict key, unknown non-alias value", false, `{"IDTAGIMPORTANCE":"Unimportant"}`, false}, - {"Known strict key, known value alias", false, `{"IDTAGIMPORTANCE":"High"}`, false}, - {"Known strict key, list of known alias values", false, `{"IDTAGIMPORTANCE":["Unimportant","High"]}`, false}, - {"Known strict key, list of unknown non-alias values", false, `{"IDTAGIMPORTANCE":["foo","bar"]}`, false}, + { + "Known first key & value; known 2nd key, unknown 2nd value", + false, + `{"IDTAGANIMALS":"IDVALANIMAL1", "IDTAGIMPORTANCE": "blah blah"}`, + false, + "tag value.*is not valid for key.*", + }, + { + "Unknown non-alias key on strict vocabulary", + true, + `{"foo":"bar"}`, + false, + "tag key.*is not defined in the vocabulary", + }, + { + "Known non-strict key, known value alias", + false, + `{"IDTAGANIMALS":"Loxodonta"}`, + false, + "tag value.*for key.* is an alias, must be provided as.*", + }, + { + "Known strict key, unknown non-alias value", + false, + `{"IDTAGIMPORTANCE":"Unimportant"}`, + false, + "tag value.*is not valid for key.*", + }, + { + "Known strict key, lowercase value regarded as alias", + false, + `{"IDTAGIMPORTANCE":"idval1"}`, + false, + "tag value.*for key.* is an alias, must be provided as.*", + }, + { + "Known strict key, known value alias", + false, + `{"IDTAGIMPORTANCE":"High"}`, + false, + "tag value.* for key.*is an alias, must be provided as.*", + }, + { + "Known strict key, list of known alias values", + false, + `{"IDTAGIMPORTANCE":["High", "Low"]}`, + false, + "tag value.*for key.*is an alias, must be provided as.*", + }, + { + "Known strict key, list of unknown non-alias values", + false, + `{"IDTAGIMPORTANCE":["foo","bar"]}`, + false, + "tag value.*is not valid for key.*", + }, + { + "Invalid value type", + false, + `{"IDTAGANIMALS":1}`, + false, + "value type for tag key.* was.*, but expected a string or list of strings", + }, + { + "Value list of invalid type", + false, + `{"IDTAGANIMALS":[1]}`, + false, + "value list element type for tag key.* was.*, but expected a string", + }, } for _, tt := range tests { c.Log(c.TestName()+" ", tt.name) @@ -95,6 +197,7 @@ func (s *VocabularySuite) TestCheck(c *check.C) { c.Assert(err, check.IsNil) } else { c.Assert(err, check.NotNil) + c.Assert(err.Error(), check.Matches, tt.errMatches) } } } @@ -119,7 +222,8 @@ func (s *VocabularySuite) TestNewVocabulary(c *check.C) { "labels": [{"label": "Animal"}, {"label": "Creature"}], "values": { "IDVALANIMAL1":{"labels":[{"label":"Human"}, {"label":"Homo sapiens"}]}, - "IDVALANIMAL2":{"labels":[{"label":"Elephant"}, {"label":"Loxodonta"}]} + "IDVALANIMAL2":{"labels":[{"label":"Elephant"}, {"label":"Loxodonta"}]}, + "DOG":{"labels":[{"label":"Dog"}, {"label":"Canis lupus familiaris"}, {"label":"dOg"}]} } } }}`, @@ -134,6 +238,8 @@ func (s *VocabularySuite) TestNewVocabulary(c *check.C) { "docker-image-repo-tag": true, "filters": true, "container_request": true, + "cwl_input": true, + "cwl_output": true, }, StrictTags: false, Tags: map[string]VocabularyTag{ @@ -147,20 +253,35 @@ func (s *VocabularySuite) TestNewVocabulary(c *check.C) { "IDVALANIMAL2": { Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "Loxodonta"}}, }, + "DOG": { + Labels: []VocabularyLabel{{Label: "Dog"}, {Label: "Canis lupus familiaris"}, {Label: "dOg"}}, + }, }, }, }, }, }, { - "Valid data, but uses reserved key", + "Invalid JSON error with line & column numbers", + `{"tags":{ + "aKey":{ + "labels": [,{"label": "A label"}] + } + }}`, + false, `invalid JSON format:.*\(line \d+, column \d+\)`, nil, + }, + { + "Invalid JSON with duplicate & reserved keys", `{"tags":{ "type":{ "strict": false, - "labels": [{"label": "Type"}] + "labels": [{"label": "Class", "label": "Type"}] + }, + "type":{ + "labels": [] } }}`, - false, "tag key.*is reserved", nil, + false, "(?s).*duplicate JSON key \"tags.type.labels.0.label\"\nduplicate JSON key \"tags.type\"\ntag key \"type\" is reserved", nil, }, } @@ -183,14 +304,14 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { tests := []struct { name string voc *Vocabulary - errMatches string + errMatches []string }{ { "Strict vocabulary, no keys", &Vocabulary{ StrictTags: true, }, - "vocabulary is strict but no tags are defined", + []string{"vocabulary is strict but no tags are defined"}, }, { "Collision between tag key and tag key label", @@ -207,7 +328,7 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "", // Depending on how the map is sorted, this could be one of two errors + nil, // Depending on how the map is sorted, this could be one of two errors }, { "Collision between tag key and tag key label (case-insensitive)", @@ -224,7 +345,7 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "", // Depending on how the map is sorted, this could be one of two errors + nil, // Depending on how the map is sorted, this could be one of two errors }, { "Collision between tag key labels", @@ -241,7 +362,7 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "tag label.*for key.*already seen.*", + []string{"(?s).*tag label.*for key.*already seen.*"}, }, { "Collision between tag value and tag value label", @@ -262,7 +383,7 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "", // Depending on how the map is sorted, this could be one of two errors + nil, // Depending on how the map is sorted, this could be one of two errors }, { "Collision between tag value and tag value label (case-insensitive)", @@ -283,7 +404,7 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "", // Depending on how the map is sorted, this could be one of two errors + nil, // Depending on how the map is sorted, this could be one of two errors }, { "Collision between tag value labels", @@ -304,7 +425,28 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "tag value label.*for pair.*already seen.*", + []string{"(?s).*tag value label.*for pair.*already seen.*on value.*"}, + }, + { + "Collision between tag value labels (case-insensitive)", + &Vocabulary{ + StrictTags: false, + Tags: map[string]VocabularyTag{ + "IDTAGANIMALS": { + Strict: false, + Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}}, + Values: map[string]VocabularyTagValue{ + "IDVALANIMAL1": { + Labels: []VocabularyLabel{{Label: "Human"}, {Label: "Mammal"}}, + }, + "IDVALANIMAL2": { + Labels: []VocabularyLabel{{Label: "Elephant"}, {Label: "mAMMAL"}}, + }, + }, + }, + }, + }, + []string{"(?s).*tag value label.*for pair.*already seen.*on value.*"}, }, { "Strict tag key, with no values", @@ -317,15 +459,47 @@ func (s *VocabularySuite) TestValidationErrors(c *check.C) { }, }, }, - "tag key.*is configured as strict but doesn't provide values", + []string{"(?s).*tag key.*is configured as strict but doesn't provide values"}, + }, + { + "Multiple errors reported", + &Vocabulary{ + StrictTags: false, + Tags: map[string]VocabularyTag{ + "IDTAGANIMALS": { + Strict: true, + Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Creature"}}, + }, + "IDTAGSIZES": { + Labels: []VocabularyLabel{{Label: "Animal"}, {Label: "Size"}}, + }, + }, + }, + []string{ + "(?s).*tag key.*is configured as strict but doesn't provide values.*", + "(?s).*tag label.*for key.*already seen.*", + }, }, } for _, tt := range tests { c.Log(c.TestName()+" ", tt.name) - err := tt.voc.validate() + validationErrs, err := tt.voc.validate() c.Assert(err, check.NotNil) - if tt.errMatches != "" { - c.Assert(err, check.ErrorMatches, tt.errMatches) + for _, errMatch := range tt.errMatches { + seen := false + for _, validationErr := range validationErrs { + if regexp.MustCompile(errMatch).MatchString(validationErr) { + seen = true + break + } + } + if len(validationErrs) == 0 { + c.Assert(err, check.ErrorMatches, errMatch) + } else { + c.Assert(seen, check.Equals, true, + check.Commentf("Expected to see error matching %q:\n%s", + errMatch, strings.Join(validationErrs, "\n"))) + } } } }